Question

Scala 2.10 introduces value classes, which you specify by making your class extend AnyVal. There are many restrictions on value classes, but one of their huge advantages is that they allow extension methods without the penalty of creating a new class: unless boxing is required e.g. to put the value class in an array, it is simply the old class plus a set of methods that take the class as the first parameter. Thus,

implicit class Foo(val i: Int) extends AnyVal {
  def +*(j: Int) = i + j*j
}

unwraps to something that can be no more expensive than writing i + j*j yourself (once the JVM inlines the method call).

Unfortunately, one of the restrictions in SIP-15 which describes value classes is

  1. The underlying type of C may not be a value class.

If you have a value class that you can get your hands on, say, as a way to provide type-safe units without the overhead of boxing (unless you really need it):

class Meter(val meters: Double) extends AnyVal {
  def centimeters = meters*100.0                // No longer type-safe
  def +(m: Meter) = new Meter(meters+m.meters)  // Only works with Meter!
}

then is there a way to enrich Meter without object-creation overhead? The restriction in SIP-15 prevents the obvious

implicit class RichMeter(m: Meter) extends AnyVal { ... }

approach.

Was it helpful?

Solution

In order to extend value classes, you need to recapture the underlying type. Since value classes are required to have their wrapped type accessible (val i not just i above), you can always do this. You can't use the handy implicit class shortcut, but you can still add the implicit conversion longhand. So, if you want to add a - method to Meter you must do something like

class RichMeter(val meters: Double) extends AnyVal {
  def -(m: Meter) = new Meter(meters - m.meters)
}
implicit def EnrichMeters(m: Meter) = new RichMeter(m.meters)

Note also that you are allowed to (freely) rewrap any parameters with the original value class, so if it has functionality that you rely on (e.g. it wraps a Long but performs complicated bit-mixing), you can just rewrap the underlying class in the value class you're trying to extend wherever you need it.

(Note also that you'll get a warning unless you import language.implicitConversions.)

Addendum: in Scala 2.11+, you may make the val private; for cases where this was done, you will not be able to use this trick.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top