4

Hi I am studying the Advanced Scala book, and I have some trouble understading this piece of code from scalaz source:

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]

  // ...
}

How can it work? a.asInstanceOf[A @@ T] should fail with ClassCastException shouldn't it?

An example of usage is:

Multiplication(2) |+| Multiplication(3) 

In this case a is an Int how can it be converted to a @@[Int, Multiplication] (Tagged[Int, Multiplication])

Thanks for the help.

Apocalisp
  • 34,834
  • 8
  • 106
  • 155
Filippo De Luca
  • 704
  • 5
  • 21

1 Answers1

12

This works because of erasure. @@ is a purely type-level construct, meaning it has no runtime representation.

The type A @@ T is an alias for the type AnyRef{type Tag = T; type Self = A}. And since Int can be safely cast to AnyRef (under the hood this is done via casting a java.lang.Integer to a java.lang.Object), this works just fine.

The additional structure {type Tag = T; type Self = A} only exists at compile-time, so it has been completely erased by the time the JVM does the cast.

Why do this? The purpose of @@ (which I pronounce "qua") is to create a new type from an old one, without incurring a runtime overhead.

If we used, for example, case class Multiplication(value: Int), this allows us to treat Multiplication as distinct from Int, but it creates an actual Multiplication object at runtime.

If we used a type alias like type Multiplication = Int, then there is no runtime overhead. But now Multiplication is indistinguishable from an Int, which is not what we want.

The type Int @@ Multiplication prevents us from using a value of this type directly as an Int, even though it really is just an Int at runtime.

Apocalisp
  • 34,834
  • 8
  • 106
  • 155
  • Thanks now it makes sense. It is basically a pre Scala 2.10 value class. – Filippo De Luca May 03 '15 at 14:18
  • This is strictly more general than value classes, since the type `A` in `A @@ T` can be any type whatsoever, not just a subtype of `AnyVal` as in value classes. – Apocalisp May 03 '15 at 14:21
  • 1
    @Apocalisp: No, value classes can be used to wrap types which are not a subtype of AnyVal, like `class Foo(val underlying: String) extends AnyVal`. But value classes can result in runtime overhead in some cases, see http://docs.scala-lang.org/overviews/core/value-classes.html . On the other hand, the tagged type approach will always result in runtime overhead for primitive types (`Int` becomes a JVM `int` but `Int @@ Foo` becomes an `Integer`). – Guillaume Martres May 10 '15 at 22:43