如何解决通过类型参数对 ADT 列表进行类型安全过滤
考虑以下示例:
我。
class A
class B
sealed trait Test {
type Meta
}
case class Test1() extends Test {
type Meta = A
}
case class Test2() extends Test {
type Meta = B
}
case class Info[T <: Test](t: T,m: T#Meta)
//Filters List[Info[_ <: Test]] by a generic type T <: Test and returns List[Info[T]]
def filter[T <: Test: ClassTag](lst: List[Info[_ <: Test]])(
implicit ev: ClassTag[T#Meta]): List[Info[T]] =
lst.collect {
case Info(t: T,a: T#Meta) =>
val i: Info[T] = Info[T](t,a)
i
}
让我陷入困境的是 PartialFunction
的 case
是否详尽无遗。我尝试完全模式匹配 Info[_ <: Test]
如下:
二。
val t: Info[_ <: Test] = ???
t match {
case Info(t: Test1,a: Test1#Meta) =>
println("1")
case Info(t: Test2,a: Test2#Meta) =>
println("2")
}
并收到以下(非常可怕的)警告:
match may not be exhaustive.
It would fail on the following inputs:
Info((x: _$2 forSome x not in (Test1,Test2)),(x: _$2#Meta forSome x not in (A,B))),Info((x: _$2 forSome x not in (Test1,??),A()),B()),Info(??,Info(Test1(),Info(Test2(),A())
问题:我在语义或一些奇怪的{{1}错过了}?
解决方法
好吧,如您所见,您有一个建议的破坏案例列表。例如,Info(Test1(),B())
。让我们构建其中之一。
与 def foo(t: Test)(meta: t.Meta) = ???
中的路径相关类型不同,在 case class Info
中使用的类型投影不会对 T = Test1 => T#Meta = A
的含义进行编码。虽然我需要一个简单的实用方法来说服 scalac 推断出我想要的类型。
def castMeta[T <: Test](t: T#Meta): Test#Meta = t
有了它,我们就可以从 A 和 B 中获取类型为 Test#Meta 的值。
所以,
val t: Info[_ <: Test] = Info[Test](Test1(),castMeta[Test2](new B))
将在运行时导致 MatchError
,并使其不涉及任何“黑客”,如空值、asInstanceOf
或 ClassTag
。
另请注意,由于稳健性问题,Type#Member
形式的一般类型投影计划在 Scala 3 中移除。
编辑:请注意,由于您自己提供目标类型对,因此您的过滤器可能会执行您想要的操作,只是您的模型完全允许 scalac 警告您的奇怪情况: )
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。