February 17
I think we’ve gone over this discussion here and in here. The gist is that they are something unique (otherwise we wouldn’t be talking about them) that can be viewed in different ways:
- Compile-time wrappers; meaning, they should have better performance than plain wrappers, but on the other hand cannot be instance-checked (as it is a runtime capability).
- Abstract type members that are “instantiated” (aliased) only in their local scope (and most likely add functionality via extension methods).
Seeing how their motivation is mainly to implement value classes more efficiently than
AnyVal
, it’s easier for me to think of them as wrappers.
That’s like saying that if the motivation for function programming is to be safer than imperative programming, then functional programming is a kind of imperative programming.
Opaque types are not a way to implement value classes. They are a different solution than value classes.
Wrapper means you have something inside something else. In the context of classes that means a field inside a class. Value classes are that. You have a class that has a single field, so it’s wrapping it. And if it extends AnyVal then the compiler does some magic to make that extra layer of the wrapping class not get in the way as much possible, because normally by definition a wrapper is an extra layer and so gets in the way to some extent. That is why it has a performance impact. And the AnyVal approach is not so efficient because often there’s no way the compiler can make the wrapper be a fiction, and it has to remain a wrapper at runtime, with the resulting cost.
The whole entire point of opaque types is to skip the whole wrapper idea completely. We don’t pretend we are putting something inside something, so there is no outer something for the compiler to eliminate. Instead we just work with the values directly.
The only thing opaque types have in common with wrapper classes is that they serve a similar goal, which is “how do get this same value to have a low-level type in some scopes and a high-level type in some other scope.”
Value classes achieve this by wrapping the low-level value inside a class, which serves as the high-level type. This requires making no fundamental change to the type system, since the wrapper is supposedly a different value, and different values can always have different types. However generating the code to run is much trickier because we have to make one value act like it’s some other value that shouldn’t really exist.
Opaque types achieve the goal instead by modifying the type system with a new concept of being able to put boundaries on the validity of a type. Within one scope a value has one type, and within another scope the same value has a different type. This is a new feature of the type system. But the runtime code is identical to as with “transparent” type aliases (or no type alias at all).