Scalaz `Tag.apply`: 它是如何工作的?

4

你好,我正在学习高级Scala教材,并在尝试理解scalaz源代码中的某一段代码时遇到了困难:

object Tag {
  /** `subst` specialized to `Id`.
    *
    * @todo According to Miles, @specialized doesn't help here. Maybe manually specialize.
    */
  @inline def apply[@specialized A, T](a: A): A @@ T = a.asInstanceOf[A @@ T]

  // ...
}

它是如何工作的呢?a.asInstanceOf[A @@ T] 应该会抛出 ClassCastException,不是吗?

使用示例:

Multiplication(2) |+| Multiplication(3) 

在这种情况下,a是一个Int类型,如何将其转换为@@[Int, Multiplication] (Tagged[Int, Multiplication])类型?
感谢您的帮助。

“Advanced Scala book”是什么?我在谷歌上没有找到这样的书。 - Kevin Meredith
1
这是来自Underscore的一本书:http://underscore.io/training/courses/advanced-scala-scalaz - Filippo De Luca
1个回答

12
这是因为擦除的缘故。 @@ 是一个纯粹的类型级别构造,意味着它没有运行时表示。
类型A @@ T是类型AnyRef {type Tag = T; type Self = A}的别名。由于可以将Int安全地转换为AnyRef(在底层,这是通过将java.lang.Integer强制转换为java.lang.Object来完成的),所以这个方法完全可行。
附加结构{type Tag = T; type Self = A}仅存在于编译时,因此在JVM执行转换时已经被完全擦除。
为什么要这样做?@@(我发音为“qua”)的目的是从旧的类型中创建一个新类型,而不会产生运行时开销。
例如,如果我们使用case class Multiplication(value: Int),这将允许我们将Multiplication视为与Int不同的类型,但它会在运行时创建一个实际的Multiplication对象。
如果我们像type Multiplication = Int那样使用类型别名,则没有运行时开销。但现在MultiplicationInt无法区分,这不是我们想要的。
类型Int @@ Multiplication防止我们直接使用此类型的值作为Int,尽管在运行时它确实只是一个Int

谢谢,现在我明白了。它基本上是 Scala 2.10 之前的值类。 - Filippo De Luca
这比值类严格更通用,因为A @@ T中的类型A可以是任何类型,而不仅仅是像值类中的AnyVal子类型。 - Apocalisp
1
@Apocalisp: 不是的,价值类可以用来封装不是AnyVal子类型的类型,比如 class Foo(val underlying: String) extends AnyVal。但是在某些情况下,价值类可能会导致运行时开销,请参见http://docs.scala-lang.org/overviews/core/value-classes.html。另一方面,标记类型方法对于原始类型总是会产生运行时开销(`Int`变成JVM的`int`,但`Int @@ Foo变成了Integer`)。 - Guillaume Martres

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接