-
Notifications
You must be signed in to change notification settings - Fork 21
Description
Noting for posterity, because I tried for this during the development of value classes, and I don't doubt it's still wontfix:
Value classes are much less useful than they could have been. The prohibition on wrapping means that you can take exactly one step down any given value class path and are frozen there forever after. If you try to use value classes in a meaningful way you will run into this restriction constantly. A nontrivial design which is not hampered is not possible.
AFAIK the only reason for the restriction is the requirement that everything be reconstructable at runtime via pattern matching. It is kind of tragic to so severely limit the power of value classes to support typeless-language style programming. If you need to pattern match your way back out of nested value classes, you are Doing It Wrong.
Here is a typical example of where it hits, even before a second API-facing value class appears. Your value class can have non-value extension methods; your non-value class can have value class extension methods; but if you would like your value class to have value class extension methods (and when exactly would you NOT want this for your value class? "Never" is when) you cannot.
object a {
final class Meter(val amount: Int) extends AnyVal {
def add(x: Meter) = new Meter(amount + x.amount)
}
final implicit class MeterOps(val meter: Meter) extends AnyRef {
def bippy(x: Meter) = new Meter(meter.amount * x.amount)
}
def f(x: Meter) = (x add x) -> (x bippy x)
}
object b {
final class Meter(val amount: Int) extends AnyRef {
def add(x: Meter) = new Meter(amount + x.amount)
}
final implicit class MeterOps(val meter: Meter) extends AnyVal {
def bippy(x: Meter) = new Meter(meter.amount * x.amount)
}
def f(x: Meter) = (x add x) -> (x bippy x)
}
object c {
final class Meter(val amount: Int) extends AnyVal {
def add(x: Meter) = new Meter(amount + x.amount)
}
final implicit class MeterOps(val meter: Meter) extends AnyVal {
def bippy(x: Meter) = new Meter(meter.amount * x.amount)
}
// ./a.scala:25: error: value class may not wrap another user-defined value class
// final implicit class MeterOps(val meter: Meter) extends AnyVal {
// ^
// one error found
def f(x: Meter) = (x add x) -> (x bippy x)
}