有没有一种方法可以扩展 Scala 中存在类型量词的范围,以说服类型检查器两个变量具有相同的类型?
Is there a way to expand the scope of an existential type quantifier in Scala to convince the type checker that two variables have the same type?
考虑以下代码片段:
case class Foo[A](a:A)
case class Bar[A](a:A)
def f[B](foo:Foo[Seq[B]], bar:Bar[Seq[B]]) = foo.a ++ bar.a
val s : Seq[T] forSome {type T} = Seq(1, 2, 3)
f(Foo(s), Bar(s))
最后一行未能通过类型检查,因为 Foo(s)
的类型为 Foo[Seq[T]] forSome {type T}
而 Bar(s)
的类型为 Bar[Seq[T]] forSome {type T}
,即它们都有自己的存在量词。
有什么办法解决这个问题吗?实际上,我在编译时所知道的 s
就是它具有这种存在类型。如何强制 Foo(s)
和 Bar(s)
属于单个存在量词的范围?
这有意义吗?我对 Scala 和一般的奇特类型还很陌生。
要清楚,
val s : Seq[T] forSome {type T} = Seq(1, 2, 3)
等同于
val s: Seq[_] = Seq(1, 2, 3)
我认为这个问题的答案是否定的。您需要使用范围内的类型 parameter/type 成员或具体类型。
一种方法是使用标记类型:http://etorreborre.blogspot.com/2011/11/practical-uses-for-unboxed-tagged-types.html
type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
def tag[A, B](a: A): @@[A, B] = a.asInstanceOf[@@[A, B]]
trait ThisExistsAtCompileTime
case class Foo[A](a:A)
case class Bar[A](a:A)
def f[B](foo:Foo[Seq[B]], bar:Bar[Seq[B]]) = foo.a ++ bar.a
val s : Seq[@@[T, ThisExistsAtCompileTime] forSome {type T}] = Seq(1, 2, 3) map { x => tag[Any, ThisExistsAtCompileTime](x) }
f(Foo(s), Bar(s))
我意识到可以通过一点重构来完成这项工作:
case class Foo[A](a:A)
case class Bar[A](a:A)
def f[B](foo:Foo[Seq[B]], bar:Bar[Seq[B]]) = foo.a ++ bar.a
def g[B](s1:Seq[B], s2:Seq[B]) = f(Foo(s1), Bar(s2))
val s : Seq[T] forSome {type T} = Seq(1, 2, 3)
g(s)
基本上,我将对 f
的调用包装在另一个函数 g
中,以保证两个序列具有相同的类型。
考虑以下代码片段:
case class Foo[A](a:A)
case class Bar[A](a:A)
def f[B](foo:Foo[Seq[B]], bar:Bar[Seq[B]]) = foo.a ++ bar.a
val s : Seq[T] forSome {type T} = Seq(1, 2, 3)
f(Foo(s), Bar(s))
最后一行未能通过类型检查,因为 Foo(s)
的类型为 Foo[Seq[T]] forSome {type T}
而 Bar(s)
的类型为 Bar[Seq[T]] forSome {type T}
,即它们都有自己的存在量词。
有什么办法解决这个问题吗?实际上,我在编译时所知道的 s
就是它具有这种存在类型。如何强制 Foo(s)
和 Bar(s)
属于单个存在量词的范围?
这有意义吗?我对 Scala 和一般的奇特类型还很陌生。
要清楚,
val s : Seq[T] forSome {type T} = Seq(1, 2, 3)
等同于
val s: Seq[_] = Seq(1, 2, 3)
我认为这个问题的答案是否定的。您需要使用范围内的类型 parameter/type 成员或具体类型。
一种方法是使用标记类型:http://etorreborre.blogspot.com/2011/11/practical-uses-for-unboxed-tagged-types.html
type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
def tag[A, B](a: A): @@[A, B] = a.asInstanceOf[@@[A, B]]
trait ThisExistsAtCompileTime
case class Foo[A](a:A)
case class Bar[A](a:A)
def f[B](foo:Foo[Seq[B]], bar:Bar[Seq[B]]) = foo.a ++ bar.a
val s : Seq[@@[T, ThisExistsAtCompileTime] forSome {type T}] = Seq(1, 2, 3) map { x => tag[Any, ThisExistsAtCompileTime](x) }
f(Foo(s), Bar(s))
我意识到可以通过一点重构来完成这项工作:
case class Foo[A](a:A)
case class Bar[A](a:A)
def f[B](foo:Foo[Seq[B]], bar:Bar[Seq[B]]) = foo.a ++ bar.a
def g[B](s1:Seq[B], s2:Seq[B]) = f(Foo(s1), Bar(s2))
val s : Seq[T] forSome {type T} = Seq(1, 2, 3)
g(s)
基本上,我将对 f
的调用包装在另一个函数 g
中,以保证两个序列具有相同的类型。