Programmers that came from typed languages such as Java or Scala “in dynamic langs it is easier but less secure” sometimes want to convert types or functions.
When we try to convert simplest types, for example:
val minorNum = (x: Int) => x.toLong println(minorNum(minor).getClass)
there’s no complications to worry about, or also in a down casting situation, such:
val largerNum = (x: Long) => x.toInt println(largerNum(larger).getClass)
There isn’t any problem about that, unless the loss of precision in the conversion.
Scala has a ultimate powerful feature called Implicit Conversions that allows most complex conversions. Let’s see an example:
Let’s suppose to we have a Class that encapsulate a number to calculate it Rational based in a parameterized denominator.
class Rational(n: Int, d: Int)
In order to simplify the Rational creation, our class has a simpler constructor that takes only the number and set a default denominator, in this case 1:
“Scala custom constructors always must call the primary constructor in it first line with all necessary parameters. So custom constructors are always safe in Scala, opposite to Java, where this control is manual.”
def this(n: Int) = this(n ,1)
It means is possible to create Rational instances like:
val oneHalf = new Rational(3) val twoThirds = new Rational(2,3)
With our Rationals, maybe we would like to calculate these numbers:
val r = oneHalf / twoThirds
In Scala, above line not compile because Rational class don’t has / method, but don’t worry, we will define them:
def / (that: Rational): Rational = new Rational(numer * that.denom, denom * that.numer)
def * (that: Rational): Rational = new Rational( numer * that.numer, denom * that.denom)
def - (that: Rational): Rational = new Rational( numer * that.denom - that.numer * denom, denom * that.denom )
def + (that: Rational): Rational = new Rational( numer * that.denom + that.numer * denom, denom * that.denom )
With above methods, we call calculate our Rational freely:
oneHalf / twoThirds res1: Rational = 9/2
Ok, that like so beauty and immutable, but suppose to we would like to use the Rational class in a more natural an native style, such this kind of operation:
val r = new Rational(2,3) 2 * r
Above we are trying to multiply the number 2 by Rational object r. Obviously this is not possible because the * method of Scala Int Class accepts a diferente parameter, and * method of Rational class vice versa.
To solve this situation, we need to use some
black magic, and this is what this posting is about, let’s see how we can solve that with Scala Implicit Conversion:
implicit def intToRational(x: Int) = new Rational(x)
The above code defines a implicit conversion of a Int type to a Rational type, it means that now is possible to calculate in this way:
val r = new Rational(2,3) 2 * 3 res7: Rational = 4/3
Implicit conversions can help you to design libraries mostly concise, that’s true. Scala gives us great deal of power, but before start to explore this kind of advanced features, keep in mind that with power comes responsibility, because Implicit conversions can be confused or impossible to understand in a very large code base.
You can see the complete code of this example in the following link: