Copyright © 2009, Jorge Ortiz and David Hall
A minimal language construct is proposed that allows for more concise use of the common "Pimp My Library" pattern. This construct permits the compiler to eliminate object creation so long as the transformation preserves the semantics of the original program. In addition, an annotation is proposed for the standard library that verifies such an optimization was performed.
The popular Pimp My Library pattern is used in Scala to extend pre-existing classes with new methods, fields, and interfaces.
The main drawback it suffers is that every invocation of the implicit conversion involves object creation. This makes the pattern unsuitable for use in performance-critical code. In these situations it is common to remove use of the pattern and resort to using an object with static helper methods. In many cases, this is a simple mechanical transformation that could be performed by an optimizing compiler.
A secondary drawback is that the pattern is fairly verbose. It requires both a class definition and an implicit method definition. These definitions can occur separately, possibly causing confusion about the location of an extension class's conversion method or vice versa. In addition, both definitions must be documented.
An implicit class must have a primary constructor with exactly one argument in its first parameter list. It may also include an additional implicit parameter list. Like case classes, an implicit class definition will add methods to its companion class, and define it if it hasn't already been defined. Only one method, apply
, is added to the companion object, in the same way as it is added to a case class's companion object. Unlike with case classes, this apply
method is declared implicit. This method will be referred to as the implicit conversion.
For example, a definition of the form:
implicit class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
will be transformed by the compiler as follows:
class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
object RichInt {
implicit def apply(n: Int): RichInt = new RichInt(n)
}
With implicit class definitions, the compiler may transform code that uses the implicit conversion in order to eliminate object creation, so long as the transformation preserves the semantics of the original program.
In addition, a class annotation @sreplace
(for "scalar replacement") would be added as part of the standard library. If a member of an implicit class is annotated with it, the compiler must optimize access to that member (i.e., avoid object creation) or else emit an error. If an implicit class is annotated with it, the compiler must optimize access to all that class's members or else emit an error. This annotation may only be used on implicit classes and their members.
No changes are required to Scala's syntax specification, as the relevant production rules already allow for implicit classes.
LocalModifier ::= ‘implicit’
BlockStat ::= {LocalModifier} TmplDef
TmplDef ::= [‘case’] ‘class’ ClassDef
The language specification (SLS 7.1) would be modified to allow the use of the implicit modifier for classes, even at the top-level. A new section on Implicit Classes would describe the behavior of the construct.
One design consideration is that the name of the implicit class may be imported without also importing its implicit conversion. For the example above, import RichInt
would import the name of the class, but not the implicit conversion. In contrast, import RichInt._
would import the implicit conversion, making available the methods defined in RichInt.
Another design consideration is to give compiler implementers as much flexibility as possible when optimizing implicit classes. In particular, compilers are allowed to implement minimal or no optimizations. This allows for improvements to the compiler to be added at later dates with no further changes to the specification.
The details of optimizations are purposefully left to the compiler implementer, but the authors envision as feasible transforming the following implicit class and it's usage, which would normally involve object creation:
implicit class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
import RichInt._
val i = 3 min 5
Into the following code, free of object creation:
class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = RichInt.methods$.min(n, m)
...
}
object RichInt {
implicit def apply(n: Int): RichInt = new RichInt(n)
<synthetic> object methods$ {
def min(n: Int, m: Int): Int = if (n <= m) n else m
...
}
}
val i = RichInt.methods$.min(3, 5)
The methods in the implicit class are implemented as static methods of a synthetic object. The compiler can then elide the use of the implicit conversion, avoid object creation, and use the static method instead. Since these optimizations are optional, the compiler can place arbitrary restrictions on which classes can be optimized (e.g., no var
s, no escaped references to this
). The only restriction is that the semantics of the program must not change if optimizations occur.