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
Any which would get the
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
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
Classes extending either
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 does not have any methods. Because it has no
== 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
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.