Implicit conversions in Scala help with DRY

Photo by Chinh Le Duc on Unsplash

Are you tired of hearing about how verbose Java is? Semicolons are mostly optional in Kotlin (and in Scala, too). Big deal. Slightly more significant are type declarations. For example:

Oracle has taken steps to cut down on that sort of verbosity in Java. Also, IntelliJ’s auto-complete makes it a breeze to put in declarations like that (NetBeans not so much).

In Scala, there are much more significant ways in which the language helps you cut down on repetition.

One of those ways is that Scala allows you to define implicit type conversions. Another way is with default parameters, which I will briefly address later on.

I think I first read about implicit type conversions in C++ a couple of decades ago, if I recall correctly. Or it could have been about implicit type conversions in C#, but much more recently.

The concept didn’t really make sense to me until I read Cay Horstmann’s Scala for the Impatient, First Edition (the Second Edition is available now a couple of different ways, each through the author’s website).

For several concepts, Horstmann uses fractions as examples. Fractions make for much better examples than most toy examples he or I or anyone else can think of.

I suppose for some people fractions might seem like a toy example, but even so, they are easy to understand and I think they make it very easy to see how the concept could be applied to an actual program that you’re working on.

And unlike complex numbers, there is no need to stop to explain a simple mathematical concept that most people are unfamiliar with. Though I have seen complex numbers used to explain implicit conversions.

An integer is a fraction that happens to have 1 for a denominator (likewise, a real number is a complex number that happens to have 0 for an imaginary part).

If we have a Fraction class in our program, we naturally want to be able to do arithmetic with fractions and integers. We want to be able to do things like subtract 8 from 73/4 (that should be 41/4).

Here’s an excerpt from a Fraction implementation in Java:

Actually, I did come up with a slightly more elegant solution for my own Java program, but this is adequate for the example.

It’s a similar situation with Fraction.minus(int): we create a temporary instance of Fraction for the int and then call Fraction.minus(Fraction). And likewise for Fraction.times(int) and Fraction.divides(int).

The conversion is a bit repetitious. As you know, DRY means Don’t Repeat Yourself. How do we cut down on the repetitions of the int to Fraction conversion?

In Scala, we can define an implicit type conversion. Now follows an almost complete source listing for a Scala implementation of Fraction:

During refactoring, we might ponder whether it would be a good idea to rewrite the division function to a one-liner with this * divisor.reciprocal, and thus throw a division by zero exception in only one place instead of two.

In Java, I like to put all the constructors at the end of a class. Can’t do that in Scala, because the primary constructor is “fused” with the class declaration.

Notice that in the Scala version I renamed plus(), negate(), minus(), times() and divides() as +, unary_-, -, * and /, enabling us to operate on instances of Fraction as if they were built-in numeric primitives.

This is one of those features of Scala that we’re trusted to use in good taste when it is appropriate to do so. Meaning, for example, that we’re not going to define + to mean something counter-intuitive, like removeAll().

Just in case you’re wondering, the Fraction object and the Fraction class could be placed in the same source file even if they didn’t have the same name.

That’s another feature we’re trusted to use appropriately. Notice also that scala.language.implicitConversions gets imported for use only within the scope of the Fraction object, since the Fraction class doesn’t need it.

If you want to follow along on your computer, make sure to take care of the TODO’s. You might also have to tweak one or two line breaks.

I typed the above source listing for Fraction.scala into Notepad, compiled it on the command line with scalac and then loaded Fraction.class into the Scala REPL that I have locally on my computer.

You might notice that scalac creates two *.class files from Fraction.scala: Fraction$.class and Fraction.class. Given that the former is barely a kilobyte and the latter is slightly more than four kilobytes, I would venture to guess the former is the object and the latter is the class.

You can also follow along in Scastie (which I wrote about last week), though I recommend omitting the line package fractions and instantiating fractions as new Fraction rather than new fractions.Fraction, as you’ll see in the quotes from the local Scala REPL below.

On the local Scala REPL, I obtained the following results:

The words “Implicit conversion invoked” appear because I included that println in IntToFraction. They’re there only for the sake of this article; there would otherwise be no println there.

In Scastie, you can put these lines at the end in the main Editor area:

Once you hit Run, assuming there are no compilation errors to deal with, you will see something like this, but a bit more colorful:

The implicit conversion messages will appear in Scastie’s Console area, which is less colorful than the Editor area.

Of course we can bypass implicit conversion for some functions, if we want to. For example, for the multiplication of a fraction by an integer:

The implicit conversion will still occur if the first multiplicand is an Int rather than a Fraction. The result should be the same regardless of whether or not implicit conversion occurs.

This could have implications for programs involving non-commutative algebra, but that’s outside the scope of this article.

I seriously doubt implicit conversions cause any kind of measurable performance penalty, even with more involved types.

And in a more involved type, making changes to the “primary” functions would probably require us to also make changes to the functions with repeated conversions.

In such a case, the benefits of implicit conversion would include reduced redundancy leading to a greater facility for making changes. There are downsides to implicit conversion, but noticeably slower program performance would most likely not be one of them.

For the sake of completeness, we should check division…

This means we should also check division by zero:

Depending on your system, you might notice a little delay between the implicit conversion message and the exception message. I think this says more about the REPL than the compiler or the Java Virtual Machine.

If we changed the division function as discussed earlier, our exception message would probably have to be a lot less specific, something like “Division by zero encountered.”

At least on the REPL it would be very easy to distinguish between division by an instance of Fraction that is arithmetically equal to 0, division by an Int equal to 0 that was implicitly converted to Fraction, and trying to take the reciprocal of an instance of Fraction that is arithmetically equal to 0.

Hmm… I wonder if I can now use the implicit conversion to get the reciprocal of an Int?

Oh well, it was worth a try. Or maybe someone in the comments will let me know of an elegant way to accomplish this.

Default parameters in constructors are somewhat related to implicit conversions, but they are different concepts. You might have noticed this tidbit in the Scala implementation of Fraction shown earlier:

It’s a convenience that allows us to define instances of Fraction that are arithmetically equal to 64-bit signed integers as new Fraction(numerator) rather than new Fraction(numerator, 1), saving us two or three keystrokes.

No implicit conversion here, at least none that we defined; numberC was initialized with a default parameter in the primary constructor. In Java we could have this same convenience with a chained constructor.

It is worth noting that Java does have a few implicit conversions, like int to long. I believe this is what allowed us to use true.hashCode as the numerator parameter for the Fraction constructor even though it’s an Int rather than a Long.

Also, in Scala it’s not necessary to explicitly call toString() when concatenating into a String, and it’s not necessary in Java either. I think it’s good form anyway, though I expect some disagreement in the comments.

In summary, implicit conversions in Scala can help us avoid repeating the same type conversion in several functions that are expected to be able to handle two or more specific types.

Also, default parameters in primary constructors can help reduce the need for chained constructors.

In the Java version of Fraction, the chained constructor and the int overloads for the arithmetic functions account for just a few lines in the source.

However, it doesn’t take a stretch of the imagination to see that, in a class with a lot more properties and actions, multiple constructors and functions overloaded for explicit conversions could add up to a lot of redundancy.

The benefits of reduced redundancy through implicit conversions vastly outweigh the unlikely cost of slower program performance.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.