Skip to content

value classes crippled by nesting restriction #7685

@scabug

Description

@scabug

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)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions