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
- 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.
What are Value Classes. At a fundamental level: value classes define objects which, once created, never change their value. A variable of a value type may only be changed by re-assigning to that variable.
Value classes are a new mechanism which help to avoid allocating run time objects. AnyVal define value classes. Value classes are predefined, they coincide to the primitive kind of Java-like languages. There are nine predefined value types : Double, Float, Long, Int, Short, Byte, Char, Unit, and Boolean.
AnyVal is the root class of all value types, which describe values not implemented as objects in the underlying host system. Value classes are specified in Scala Language Specification, section 12.2. The standard implementation includes nine AnyVal subtypes: scala. Double, scala.
An implicit class is a class marked with the implicit keyword. This keyword makes the class's primary constructor available for implicit conversions when the class is in scope. Implicit classes were proposed in SIP-13.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With