Scala 2 では、Shapeless を使用して、これを行うことができます ( Scastie ):
def f[T <: Product, H <: HList](tupleOfFoos: T)(
implicit gen: Generic.Aux[T, H],
hev: LiftAll[({type E[T] = Foo =:= T})#E, H]
) = tupleOfFoos
LiftAllFoo =:= Xfor every Xinのインスタンスがあることを確認し、Hとが完全に無関係な型ではないことを確認します。genTH
Dotty では、このための一致タイプを持つ証拠パラメーターを追加できます。
type Homogenous[H, T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case H *: t => Homogenous[H, t]
case _ => Nothing
}
def f[T <: Tuple](tupleOfFoos: T)(using Homogenous[Foo, T]) = tupleOfFoos
これにより、呼び出すことはできますf((Foo(), Foo(), Foo()))が、f((1, 2, 3)).
Homogenousは、ベース ケースが の再帰マッチ タイプですEmptyTuple。タプルが空の場合、それは非Foos で埋められないため、型は になりDummyImplicit、暗黙的にスコープ内に既にあります。(H, ...)それ以外の場合は、 /のように見えるかどうかを確認します。H *: tその場合、残りのタプル ( t) も有効かどうかを確認する必要があります。その 2 番目のケースに一致しない場合、タプルが無効であることがわかります。この場合、結果はNothingになります。正気な人は暗黙の値を作成しません。
コンテキスト境界を使用する場合は、追加のカリー化された型 ( Scastie )を作成できます。
type Homo2[H] = [T <: Tuple] =>> Homogenous[H, T]
def f[T <: Tuple : Homo2[Foo]](tupleOfFoos: T) = tupleOfFoos
残念ながら、単一のカリー化された型 ( Scastie )で動作させることができませんでした。
type Homogenous[H] = [T <: Tuple] =>> T match {
case EmptyTuple => DummyImplicit
case H *: t => Homogenous[H][t]
case _ => Nothing
}