I have been playing lately with compound types and I recently was trying the following code:
import scala.reflect._
object Main {
def main(args: Array[String]) {
val a1 = classTag[Int with String with Double].newArray(3)
println(a1.mkString(" "))
val a2 = classTag[String with Int with Double].newArray(3)
println(a2.mkString(" "))
val a3 = classTag[Double with Int with String].newArray(3)
println(a3.mkString(" "))
}
}
With the following output:
0 0 0
null null null
0.0 0.0 0.0
It results a bit strange to me. Each of the array elements has access to the methods of the three types: Int, String and Double. What is happening exactly behind scene here? The compound types are actually instances of the first type? Can a compound type being instanced explicitly? Their use case is only for when the types composing the compound are related through inheritance and so on? Thanks.
P.S.: I'm using Scala 2.11.4
What is happening exactly behind scene here? The compound types are actually instances of the first type?
Looking at the newArray method as defined for ClassTag:
override def newArray(len: Int): Array[T] =
runtimeClass match {
case java.lang.Byte.TYPE => new Array[Byte](len).asInstanceOf[Array[T]]
case java.lang.Integer.TYPE => new Array[Int](len).asInstanceOf[Array[T]]
/* snip */
case _ => java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]]
}
And then your type:
scala> classTag[Int with String with Double].runtimeClass
res4: Class[_] = int
It seems pretty clear how it's arriving at the conclusion to use the first type. The runtimeClass of Int with String with Double is Int, and the newArray method uses the runtimeClass to construct the new Array. So the Array is filled with default Int values. Likewise for the other order combinations.
Why is the runtime class Int? Well, Int with String with Double isn't an actual class, but Int is. The compiler's choice of which class to use can't just be arbitrary, so why not the first?
Can a compound type be instanced explicitly?
I'm not sure what you mean by this. If you think about it, the compiler kind of has to favor one type in this composition. What would an explicit Int with String with Double look like? What about Int with String with Double with Product with Process ? I don't know, either.
Each of the array elements has access to the methods of the three types: Int, String and Double
They do, and yet they don't.
scala> val arr = classTag[Int with Process].newArray(3)
arr: Array[Int with Process] = Array(0, 0, 0)
scala> arr(0).exitValue()
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Process
You're really just tricking the compiler into believing you have an Array of these types, when in fact you don't (and can't, because nothing can be a sub-type of both Int and Process).
Their use case is only for when the types composing the compound are related through inheritance and so on?
Pretty much, yes. I can't think if any situation in which you'd ever want to try to build these types directly. Their main use case is to guarantee that a type inherits from other specific types.
For example, a method that requires that a parameter inherit methods from two traits:
trait Hello { def hello = println("Hello") }
trait Bye { def bye = println("Bye") }
def greet(hb: Hello with Bye): Unit = { hb.hello; hb.bye }
class A extends Hello
class B extends Hello with Bye
scala> greet(new A)
<console>:15: error: type mismatch;
found : A
required: Hello with Bye
greet(new A)
^
scala> greet(new B)
Hello
Bye
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With