About the syntax boilerplate, maybe it could be possible to allow opaque
as a modifier for a class
to mean “an opaque type + the standard boilerplate”, similarly to how implicit class
means “a class + an implicit def”.
For example:
opaque [implicit] class T ([private] val a: A) {
{defs...}
}
Could be syntaxic sugar for:
opaque type T = A
object T {
[implicit] def apply(a: A): T = a
implicit class T$Ops ($self: T) extends AnyVal {
inline(?) [private] def a: A = t
{defs...}
}
}
Requirements of value classes would apply equally to “opaque” classes:
- A single
val
parameter
- No fields definitions, only methods
- Cannot be nested inside another class
Ideally, this
inside the opaque class body would be rewritten to $self
and have type T
.
Adding implicit
to the opaque class definition would add implicit
on the apply method, meaning that the value can be implicitly wrapped, otherwise explicit wrapping is required.
Adding private
to the constructor parameter would prevent the underlying value from being accessed directly and require explicit accessors to be defined.
Maybe we could even imagine opaque class T private ( ... )
that would mean that the apply
method synthesized on the companion object is also private, requiring custom wrapper to be defined on the companion object (eg. to perform validation).
While this syntax is arguably more complex that the single opaque type
definition, I believe it allows many common use cases of opaque type to be expressed with a lot less boilerplate. It would also be syntactically very similar to current value classes, meaning that converting code would be as easy as replacing the extends
by opaque
to get the unboxed semantic.
This syntax might also scale to future JVM-level value classes by allowing more than a single parameter in the class constructor, but who knows. This may not be a goal for this feature.
A last idea: if some people are not willing to introduce opaque
as a keyword, maybe the inline
keyword from Dotty could be used instead (an inline class
is a class that disappear at runtime), it obviously work a lot better in the inline class
than in the the inline type
version.