Wednesday, April 27, 2011

How-to In Scala: Share Implicit Type Conversions between Classes and Objects

In Scala, one of the most powerful features is implicit type conversions. The article at http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 does a pretty good job of explaining implicits. Essentially, you can define functions for the compiler to automatically insert to convert between two types and you can then use one type of object as another. It is extremely powerful!
One of the challenges with implicits is that they must be declared in a type. You cannot declare a "global implicit" conversion to be used across all code compiled with it. Therefore, it would seem that you must redeclare implicits in every class that you want to use them in.
Actually, you don't. You can place them in a common trait, the Scala equivalent of Java's interface. A trait implements mixins, which (in Java terms) are like not-completely-abstract classes. This enables pseudo-multiple inheritance to be used in Scala! Example (taken from exprtreelib code):

// ExpressionNodeTypes.scala; org.nathanmoos.magnificalc.exprtreelib
trait Implicits
{
implicit def closure2Function[T <: { def apply(x:Double):Double }](closure:T) = new Function() {
override def evaluate(x:Double):Double = closure.apply(x)
}
}
...
object Functions
{
...
def register(name:String, func:Function) = ...
...
}
// InternalFunctions.scala; org.nathanmoos.magnificalc.exprtreelib.functions
object InternalFunctions extends Implicits
{
...
def registerAll() = {
Functions.register("ln", log _)
Functions.register("log", log10 _)
Functions.register("sqrt", sqrt _)
Functions.register("sin", sin _)
Functions.register("cos", cos _)
Functions.register("tan", tan _)
Functions.register("csc", csc _)
Functions.register("sec", sec _)
Functions.register("cot", cot _)
Functions.register("sinh", sinh _)
Functions.register("cosh", cosh _)
Functions.register("tanh", tanh _)
Functions.register("acos", acos _)
Functions.register("asin", asin _)
Functions.register("atan", atan _)
}
}
Example 2:
object MyNewFunctions extends BaseClass with Implicits
{
def plusFour(arg:Double):Double = arg + 4

def registerAll() = {
Functions.register("plusFour", plusFour _)
}
}
This example, while quite useless, demonstrates that the new Implicits trait can be used to share implicit type conversions across classes. Look for more in this new series: How-to in Scala!

No comments:

Post a Comment