由于我无法控制的原因,我的方法接收元组形式的输入。这个元组应该只包含Foo
的实例,也就是说,它应该看起来像(Foo, Foo ... Foo)
,并且里面不应该有String
或Int
。我希望在编译时对此进行检查,而不是在运行时抛出异常。我怎样才能做到这一点?
以下是我目前拥有的代码,这是不对的:
def f(tupleOfFoos: Tuple): Tuple = {
for (x <- tupleOfFoos) assert(x.isInstanceOf[Foo])
mapTuple(tupleOfFoos, irrelevantFunction)
}
我对使用Shapeless或Dotty/Scala 3中引入的新功能持开放态度。
在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
LiftAll
确保H
中的每个X
都有一个Foo =:= X
的实例,gen
确保T
和H
不是完全不相关的类型。
在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))
。
CCD_ 14是一种递归匹配类型,其基本情况为CCD_。如果一个元组为空,那么它不会被非Foo
s填充,因此该类型变为DummyImplicit
,它的作用域中已有一个隐式。否则,我们检查它是否看起来像(H, ...)
/H *: t
,在这种情况下,我们需要检查元组的其余部分(t
(是否也是有效的。如果它与第二种情况不匹配,我们就知道元组是无效的,在这种情况下,结果是Nothing
,理智的人不会对其进行隐式计算。
如果你想使用上下文边界,你可以制作一个额外的curried类型(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
}