Several members in the SIP committee yesterday were worried that opaque types in https://docs.scala-lang.org/sips/opaque-types.html duplicated a significant amount of the functionality of value classes, and discussions I had today, notably with @julienrf echoed that sentiment. An earlier iteration of that proposal indeed made opaque types look like value classes (I think it was called “Unboxed value classes”). @jvican, do you still have a link to that document?
When I saw the earlier proposal, I was not convinced because the restrictions imposed on these unboxed value classes seemed ad-hoc. By contrast, I rather liked the opaque types proposal, so was on the side of people preferring it. But I have now realized that the restrictions proposed for unboxed value classes are in fact natural consequences if these new unboxed value classes extend not AnyVal
but a new, completely parametric Top
type. This very much echoes the original post by @S11001001 on The High Cost of AnyVal subclasses.
The idea of a parametric top type was brought up in Dotty issue 2047 - it’s a rather long comment thread; best search for AnyObj
to find where the discussion starts. A true top type is useful not just for type class coherence, the topic of #2047, or for unboxed value classes. It’s much more fundamental than that because it gives you theorems for free by ensuring parametricity.
In the Dotty issue, I proposed to keep Any
as the top type of Scala’s type lattice, but strip it of all its methods. Instead there would be a new type AnyObj
under Any
which would get the ==
, !=
, equals
, toString
, hashCode
, asInstanceOf
and isInstanceOf
methods. If we started from scratch that would still be my preferred scheme, but it’s probably too hard to migrate to it. So in what follows I propose to leave Any
as it is and introduce a new, fully parametric type Top
.
The top of Scala’s type hierarchy would look as follows:
Top
|
Any (= AnyVal)
|
Object (= AnyRef)
There’s no need for AnyVal
as a separate type anymore, but we can keep it around as an alias of Any
.
Classes extending either Any/AnyVal
or Top
, but not extending Object
, are called value classes. The usual value class restrictions apply to them; in particular they must be final. Traits can as before extend Any
instead of Object
, they are then called universal traits. Traits cannot extend directly the Top
type.
The Top
type does not have any methods. Because it has no isInstanceOf
or ==
method, it is impossible to pattern match on a scrutinee of Top
type. I believe that’s all we need to say about it!
It seems if we come back to the earlier proposal of unboxed value classes, all of the restrictions that were imposed are in fact consequences of the way Top
is defined. So the unboxed value class proposal now looks very natural. Value classes have to extend either Any
or Top
; if they extend Top
we have a guarantee that they can always be represented as their underlying type, no value-class specific boxing is needed.
Coming back to Top
. Currently the rule for an unbounded abstract type or type parameter such as [T]
is that it expands to [T <: Any]
. I would love to change this to [T <: Top]
, but realize that this would also cause considerable migration headaches. So for the moment I propose to leave this as is, but consider cleaning up this aspect at some later time.