在 scala 2.11+ 中如何使用单例类型作为类型证据?
In scala 2.11+ how to use exactly the singleton type as type evidence?
我正在编写一个用于复杂向量计算的数学库,其中一部分如下所示:
object Example1 {
val two = 2
val three = 3
// SU means 'special unitary group'
trait SU_n[D <: Int] {
def plus(v1: ComplexVector[D], v2: ComplexVector[D])(
implicit ev: D =:= two.type
): ComplexVector[D] = {
//TODO: some unspeakable magic here
???
}
}
class ComplexVector[D <: Int: ClassTag](xyzw: List[(Double, Double)]) {
{
assert(xyzw.size.isInstanceOf[D])
}
}
type Quaternion = ComplexVector[two.type]
val alsoTwo = 2
object SU_2 extends SU_n[two.type] {}
object SU_Also2 extends SU_n[alsoTwo.type] {}
object SU_3 extends SU_n[three.type] {}
val q = new Quaternion(List(1.0 -> 2.0, 3.0 -> 4.0))
{
val v2 = SU_2.plus(q, q)
val also_v2 = SU_Also2.plus(q, q)
}
val vec =
new ComplexVector[three.type](List(1.0 -> 2.0, 3.0 -> 4.0, 5.0 -> 6.0))
// This will break
// {
// val v3 = SU_3.plus(vec, vec)
// }
}
中缀类型=:=
用于确保函数plus
不能用于除四元数以外的向量,当我编译它时,我得到以下错误信息:
Error: type mismatch;
found : <mypackage>.Example1.two.type (with underlying type Int)
required: AnyRef
type Quaternion = ComplexVector[two.type]
奇怪的是,我在中缀 class =:=
的实现中找不到任何地方要求它的操作数是 AnyVal
,所以为什么我得到这个错误?以及如何fix/bypass它来达到要求? (即创建一个只能应用于四元数的函数)
.type
生成的单例类型并不像您想象的那样工作 - 特别是,它们不是文字类型。要查看此内容,请验证:
val two = 2
val alsoTwo = 2
type V = two.type
type W = alsoTwo.type
implicitly[V =:= W] // will fail with an implicit error
val test : V = 2 // will fail with "expression does not conform to type"
def id(x: V) : W = x //will fail with "expression does not conform to type"
从历史上看,此 .type
语法仅用于 AnyRef
,编译器不会为原始类型公开单例类型。从 2.13 开始,这已经改变,语言现在支持文字类型,但看起来 .type
的行为仍然相同。
如果2.13是一个选项,你可以写
trait SU_n[D <: Int] {
def plus(v1: ComplexVector[D], v2: ComplexVector[D])(
implicit ev: D =:= 2
): ComplexVector[D] = {
//TODO: some unspeakable magic here
???
}
}
type Quaternion = ComplexVector[2]
object SU_2 extends SU_n[2] {}
object SU_Also2 extends SU_n[2] {}
object SU_3 extends SU_n[3] {}
一切都会按预期进行。如果您需要坚持使用 2.11 或 2.12,我建议查看 shapeless 是否这是您要走的路线。
也就是说,根据我的评论,这似乎是一种非常奇怪的解决问题的方法,因为具有特征 T[A] { def foo(a: A) }
的想法只有在 foo
时才会编译19=] 是一种特殊类型,让我觉得相当病态。如果你真的想要一个只能被四元数访问的 plus
方法,你可以做类似
implicit class QuaternionSupport(quaternions: SU_n[2]) {
def plus(quaternion1: Quaternion, quaternion2: Quaternion) : Quaternion = ???
}
并且该方法不会出现在其他维度上。
我正在编写一个用于复杂向量计算的数学库,其中一部分如下所示:
object Example1 {
val two = 2
val three = 3
// SU means 'special unitary group'
trait SU_n[D <: Int] {
def plus(v1: ComplexVector[D], v2: ComplexVector[D])(
implicit ev: D =:= two.type
): ComplexVector[D] = {
//TODO: some unspeakable magic here
???
}
}
class ComplexVector[D <: Int: ClassTag](xyzw: List[(Double, Double)]) {
{
assert(xyzw.size.isInstanceOf[D])
}
}
type Quaternion = ComplexVector[two.type]
val alsoTwo = 2
object SU_2 extends SU_n[two.type] {}
object SU_Also2 extends SU_n[alsoTwo.type] {}
object SU_3 extends SU_n[three.type] {}
val q = new Quaternion(List(1.0 -> 2.0, 3.0 -> 4.0))
{
val v2 = SU_2.plus(q, q)
val also_v2 = SU_Also2.plus(q, q)
}
val vec =
new ComplexVector[three.type](List(1.0 -> 2.0, 3.0 -> 4.0, 5.0 -> 6.0))
// This will break
// {
// val v3 = SU_3.plus(vec, vec)
// }
}
中缀类型=:=
用于确保函数plus
不能用于除四元数以外的向量,当我编译它时,我得到以下错误信息:
Error: type mismatch;
found : <mypackage>.Example1.two.type (with underlying type Int)
required: AnyRef
type Quaternion = ComplexVector[two.type]
奇怪的是,我在中缀 class =:=
的实现中找不到任何地方要求它的操作数是 AnyVal
,所以为什么我得到这个错误?以及如何fix/bypass它来达到要求? (即创建一个只能应用于四元数的函数)
.type
生成的单例类型并不像您想象的那样工作 - 特别是,它们不是文字类型。要查看此内容,请验证:
val two = 2
val alsoTwo = 2
type V = two.type
type W = alsoTwo.type
implicitly[V =:= W] // will fail with an implicit error
val test : V = 2 // will fail with "expression does not conform to type"
def id(x: V) : W = x //will fail with "expression does not conform to type"
从历史上看,此 .type
语法仅用于 AnyRef
,编译器不会为原始类型公开单例类型。从 2.13 开始,这已经改变,语言现在支持文字类型,但看起来 .type
的行为仍然相同。
如果2.13是一个选项,你可以写
trait SU_n[D <: Int] {
def plus(v1: ComplexVector[D], v2: ComplexVector[D])(
implicit ev: D =:= 2
): ComplexVector[D] = {
//TODO: some unspeakable magic here
???
}
}
type Quaternion = ComplexVector[2]
object SU_2 extends SU_n[2] {}
object SU_Also2 extends SU_n[2] {}
object SU_3 extends SU_n[3] {}
一切都会按预期进行。如果您需要坚持使用 2.11 或 2.12,我建议查看 shapeless 是否这是您要走的路线。
也就是说,根据我的评论,这似乎是一种非常奇怪的解决问题的方法,因为具有特征 T[A] { def foo(a: A) }
的想法只有在 foo
时才会编译19=] 是一种特殊类型,让我觉得相当病态。如果你真的想要一个只能被四元数访问的 plus
方法,你可以做类似
implicit class QuaternionSupport(quaternions: SU_n[2]) {
def plus(quaternion1: Quaternion, quaternion2: Quaternion) : Quaternion = ???
}
并且该方法不会出现在其他维度上。