Scala 中具有 F 界类型和存在类型的编译问题
Compilation issue in Scala with F-bounded types and existential types
我正在使用 F 有界类型以便能够return 当前类型
trait Board[T <: Board[T]] {
def updated : T
}
我正在尝试编写一个使用它的通用辅助方法。
问题是:为什么以下不编译?
object BoardOps {
def updated(board: Board[_]) = {
board.updated.updated
}
}
错误是value updated is not a member of _
我已经找到了这两个解决方法。它们是等价的吗?
object BoardOps {
def updated(board: Board[_<:Board[_]]) = {
board.updated.updated
}
}
object BoardOps {
def updated[T <: Board[T]](board: T) : T = {
board.updated.updated
}
}
why the following do not compile?
使用Board[_]
作为参数类型告诉编译器"I do not care for the type of parameter inside board"。也就是说,对于编译器来说,this 是一种存在类型,它不知道有关该类型的任何细节。因此,board.updated
return 是 "unspeakable" 或 opaque 类型,因为我们告诉编译器 "throw out" 该类型信息。
I've figured out these 2 workarounds. Are they equivalent?
您之前的示例使用了一个存在类型,并限制为 Board[_]
的子类型,或者更正式地说,我们这样写:
Board[T] forSome { type T <: Board[U] }
编译器实际命名 T -> $_1
和 U -> $_2
的地方
同样,我们对内部类型参数一无所知,只知道它有一个上限 Board[_]
。您的后一个示例使用名为 T
的通用量化类型,编译器可以使用该类型将方法的 return 类型推断为特定类型 T
而不是 Any
.
回答你的问题,不,它们不等价。存在类型和普遍量化类型彼此 对偶 。有关存在主义的更多信息,请访问 What is an existential type? and https://www.drmaciver.com/2008/03/existential-types-in-scala/
它不会编译,因为一旦你写 Board[_]
,编译器就不会推断出任何关于匿名类型参数 _
.
的有用信息
有几个work-arounds(和你已经提出的不一样):
- 使用
Board[X] forSome { type X <: Board[X] }
- 使用模式匹配来推断有关类型的更多信息
使用forSome
存在量化
这可以通过 forSome
存在量化轻松解决:
import scala.language.existentials
object BoardOps_forSome {
def updated(board: Board[X] forSome { type X <: Board[X] }) = {
board.updated.updated.updated
}
}
这允许您调用 updated
无限次。
使用模式匹配
实际上,您可以使用模式匹配在不更改签名的情况下解决它。例如,这个不敬虔的构造允许您应用方法 updated
三次(无限次):
object BoardOps_patternMatch {
def updated(board: Board[_]) = {
board match {
case b: Board[x] => b.updated match {
case c: Board[y] => c.updated match {
case d: Board[z] => d.updated
}
}
}
}
}
这是因为一旦您将未知类型绑定到类型变量 x
、y
、z
,编译器就会被迫做一些额外的推理工作,并推断出它实际上必须是 x <: Board[_$?]
等等。不幸的是,它一次只进行一个步骤的推理,因为如果它试图计算最精确的类型,则类型计算将会发散。
上界和通用量化不一样
请注意,您的第一个变通方法只起作用两次:
object BoardOps_upperBound_once {
def updated(board: Board[_<:Board[_]]) = {
board.updated.updated // third .updated would not work
}
}
因此,它不等同于你的第二个解决方法,它也可以无限次工作,这里的例子是 updated
:
的三个调用
object BoardOps_genericT {
def updated[T <: Board[T]](board: T) : T = {
board.updated.updated.updated
}
}
我正在使用 F 有界类型以便能够return 当前类型
trait Board[T <: Board[T]] {
def updated : T
}
我正在尝试编写一个使用它的通用辅助方法。
问题是:为什么以下不编译?
object BoardOps {
def updated(board: Board[_]) = {
board.updated.updated
}
}
错误是value updated is not a member of _
我已经找到了这两个解决方法。它们是等价的吗?
object BoardOps {
def updated(board: Board[_<:Board[_]]) = {
board.updated.updated
}
}
object BoardOps {
def updated[T <: Board[T]](board: T) : T = {
board.updated.updated
}
}
why the following do not compile?
使用Board[_]
作为参数类型告诉编译器"I do not care for the type of parameter inside board"。也就是说,对于编译器来说,this 是一种存在类型,它不知道有关该类型的任何细节。因此,board.updated
return 是 "unspeakable" 或 opaque 类型,因为我们告诉编译器 "throw out" 该类型信息。
I've figured out these 2 workarounds. Are they equivalent?
您之前的示例使用了一个存在类型,并限制为 Board[_]
的子类型,或者更正式地说,我们这样写:
Board[T] forSome { type T <: Board[U] }
编译器实际命名 T -> $_1
和 U -> $_2
同样,我们对内部类型参数一无所知,只知道它有一个上限 Board[_]
。您的后一个示例使用名为 T
的通用量化类型,编译器可以使用该类型将方法的 return 类型推断为特定类型 T
而不是 Any
.
回答你的问题,不,它们不等价。存在类型和普遍量化类型彼此 对偶 。有关存在主义的更多信息,请访问 What is an existential type? and https://www.drmaciver.com/2008/03/existential-types-in-scala/
它不会编译,因为一旦你写 Board[_]
,编译器就不会推断出任何关于匿名类型参数 _
.
有几个work-arounds(和你已经提出的不一样):
- 使用
Board[X] forSome { type X <: Board[X] }
- 使用模式匹配来推断有关类型的更多信息
使用forSome
存在量化
这可以通过 forSome
存在量化轻松解决:
import scala.language.existentials
object BoardOps_forSome {
def updated(board: Board[X] forSome { type X <: Board[X] }) = {
board.updated.updated.updated
}
}
这允许您调用 updated
无限次。
使用模式匹配
实际上,您可以使用模式匹配在不更改签名的情况下解决它。例如,这个不敬虔的构造允许您应用方法 updated
三次(无限次):
object BoardOps_patternMatch {
def updated(board: Board[_]) = {
board match {
case b: Board[x] => b.updated match {
case c: Board[y] => c.updated match {
case d: Board[z] => d.updated
}
}
}
}
}
这是因为一旦您将未知类型绑定到类型变量 x
、y
、z
,编译器就会被迫做一些额外的推理工作,并推断出它实际上必须是 x <: Board[_$?]
等等。不幸的是,它一次只进行一个步骤的推理,因为如果它试图计算最精确的类型,则类型计算将会发散。
上界和通用量化不一样
请注意,您的第一个变通方法只起作用两次:
object BoardOps_upperBound_once {
def updated(board: Board[_<:Board[_]]) = {
board.updated.updated // third .updated would not work
}
}
因此,它不等同于你的第二个解决方法,它也可以无限次工作,这里的例子是 updated
:
object BoardOps_genericT {
def updated[T <: Board[T]](board: T) : T = {
board.updated.updated.updated
}
}