scala - 在 Scala 2.11+ 中如何准确地使用单例类型作为类型证据?

标签 scala singleton implicit singleton-type

我正在编写一个用于复杂向量计算的数学库,其中一部分如下所示:

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]

奇怪的是我在中缀类=:=的实现中找不到任何地方要求其操作数为 AnyVal ,那为什么我会收到这个错误?以及如何修复/绕过它以达到要求? (即创建一个只能应用于四元数的函数)

最佳答案

.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仅在 A 时才编译是一种特定的类型让我觉得相当病态。如果你真的想要一个 plus方法只能访问四元数,你可以做类似的事情
implicit class QuaternionSupport(quaternions: SU_n[2]) {
  def plus(quaternion1: Quaternion, quaternion2: Quaternion) : Quaternion = ???
}

并且该方法不会用于其他维度。

关于scala - 在 Scala 2.11+ 中如何准确地使用单例类型作为类型证据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56657977/

相关文章:

java - 使用对象数组在 Scala 中调用 java.lang.reflect.Method

java - 使用 Android 从 Scala 调用不同的 Java 父构造函数

java - 无法让@Singleton 做任何事情

dart - 将参数传递给单例并基于该参数定义成员

c++ - 为多线程环境实现单例的内存泄漏

scala - "Deferred inline method ` 富 ` in trait ` 富 ` cannot be invoked": Pairs

Scala 隐式转换和具有值类的 mkNumericOps

Scala:以简单的自定义类型实现 map 和 withFilter

c# - 等效的隐式运算符 : why are they legal?

scala - Scala错误建模