5

私の手に負えない理由により、私のメソッドはタプルの形式で入力を受け取ります。このタプルは のインスタンスのみを含む必要がありFooます。実行時に例外をスローするのではなく、コンパイル時にこれをチェックしたい。どうすればこれを達成できますか?(Foo, Foo ... Foo)StringInt

以下は私が現在持っているコードですが、正しくありません:

def f(tupleOfFoos: Tuple): Tuple = {
  for (x <- tupleOfFoos) assert(x.isInstanceOf[Foo])
  mapTuple(tupleOfFoos, irrelevantFunction)
}

Shapeless または Dotty/Scala 3 で導入された新機能を使用することにオープンです。

4

1 に答える 1

5

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
}
于 2020-10-14T15:22:22.337 に答える