I think you’re right.
def execute[R](): R = (new Object).asInstanceOf[R] // Behavior is similar here val r1: Int = execute[Int]() // class java.lang.Object cannot be cast to class java.lang.Integer val r2: Unit = execute[Unit]() // class java.lang.Object cannot be cast to class scala.runtime.BoxedUnit
r1 can be used as
Int, cast is necessary.
r2 can be used as
Unit. But there is only one value of type
Unit, therefore no need to cast.
val r3: Unit = execute[Int]()
r3 does not type check. But we see no warning from the compiler. And scala runtime is smart enough to discard the result. Such behavior is probably necessary for operating without explicit
return. I can imagine the following scenario:
def getInt(): Int = 1 def foo(): Unit = getInt foo()
If there was no discarding mechanism, the compiler would have to raise an error, since
Int is not a subtype of
val r4: Int = execute[Unit]()// ERROR: Type mismatch!
r4 does not type check. But in this case compiler knows that
Unit cannot be used as
Int and raises an error.
I still think cast
Unit -> Unit should not happen, because:
- There is no real value to it. Cast to
Unitis equivalent to discarding the value from the client perspective.
- It would be consistent with the discarding mechanism that works fine without generics:
// During the cast the value will be discarded in all cases. Sometimes, we'll see a warning. val u1: Unit = () val u2: Unit = new Object val u3: Unit = true // warning: a pure expression does nothing in statement position val u4: Unit = 1 // warning: a pure expression does nothing in statement position