Scala & the Three Implicits

Murray Todd Williams
6 min readMar 28, 2022

--

Classic image of the three witches
Inspired by the Three Fates (the Weird Sisters)

After writing my three-part series on Options, Try and Either, which ended with the added bonus of explaining the elusive Monad, I’ve been wanting to write another series—again, aimed at the beginning-to-medium-level Scala programmer, that would inspire people to explore another cool and power aspect of Scala centered around Typeclasses and Implicits.

This is the first of four part series. And yes, I hope it will inspire you to play around with and eventually add to your toolbox something that arguably gives Scala most of its remarkable power. This is based on a presentation I had prepared about a year ago for my local Austin Scala Meetup, and if you’re feeling impatient and want the video version of things, you can jump to my website and watch my presentation.

It was three or maybe even four years ago that I attended one of my first Austin Scala Meetups, where Cody Koeninger gave a presentation on these things called implicits. I still feel like I was watching magic being performed, but when I got home and immediately tried to use this thing in some code I was working on at the time, I made a proper mess of things. My first usage of implicits (really not more than a few dozens of code) ended up languishing in a project, never getting put to any good use. Eventually I smartly deleted them and moved on. I didn’t try using implicits again for a long time.

But I really do love programming in Scala, and it bugged me that I had this blind spot. I was also starting to see implicits showing up in some Typeclass stuff I was learning (more presentations from my local Scala Meetup meetings) so I decided it was time to figure this stuff out. As with my other three-part series, I’m going to give a presentation aimed at myself, three years ago.

The Problem(s) with Implicits

A little over a year ago, I was watching a video where Martin Odersky (the original inventor/writer of Scala) was talking about the upcoming Scala 3. In his talk, he stated that the problem with implicits that caused the most problems was that there were actually three distinct things using the implicit keyword, and the easiest of these things for a beginner to understand (implicit conversion) was also something that was the least useful and most likely to be misused and cause its own problems!

Scala 3 deals with this by changing the keyword for some of these things to from the keyword implicit to the newer given and using, and the more dangerous version of implicits would actually be made harder to use! After explaining how to do each of these three implicits, I’ll include a short section to show you how to switch to the Scala 3 syntax.

But please keep reading, because I haven’t had a proper chance to extol the virtues of implicits. They are incredibly powerful things that border on the truly magical!

Harry Potter style magic wand

The Magic Qualities of Implicits (and Typeclasses)

One of the things I love about Scala, in addition to its succinctness, is its ability for your application to morph things to such a point that your code looks like it’s written in its own custom DSL (domain specific language). The reader of your code is no longer thinking about Scala constructs but is reading something that more closely resembles the business domain logic that’s at the core of the problem.

Another thing I love about Scala is that you are actively discouraged from writing boilerplate code. As you’ll see in the section on Typeclasses, if you have to write some logic that deals with collections of Integers, followed by some nearly-identical logic that deals with collections of Doubles, followed by some nearly-identical logic (where each line could pretty much be copied-and-pasted) that deals with collections of Longs, then you are doing something wrong. After decades of writing code, I’m so used to writing boilerplate (how about old Java get and set accessors?) and I hated doing it, but following these patterns was considered the best practice. A lot of modern languages depend on macros which are essentially programs that pump out more boilerplate code for your program.

Hopefully I’ve piqued your curiosity. In this first article, I’m going to do show you the First Implicit because it’s the easier to understand. It’ll give you a quick taste for what it’s like to have the Scala compiler automatically do things for you.

Implicit Conversion

As I mentioned, implicit conversions are a sort of black magic that can be easily misused. In Scala 2.11, you were welcome to use it anywhere, but in 2.12 you would get a warning if you didn’t enable the feature explicitly, and in 2.13 it simply won’t work unless you enable the feature. So we’ll start our example with a simple:

import scala.language.implicitConversions

Now for a slightly contrived example, let’s say we have two case classes, one for storing a temperature in Fahrenheit and another for storing temperatures in Celsius.

case class Fahrenheit(degrees: Double)
case class Celsius(degrees: Double)

We can create some implicit methods that convert between these two classes:

implicit def f2c(f: Fahrenheit): Celsius = 
Celsius((f.degrees - 32.0) * (5.0 / 9.0))
implicit def c2f(c: Celsius): Fahrenheit =
Fahrenheit((c.degrees * 9.0 / 5.0) + 32.0)

Now that we’ve implemented these functions, if we run into any code that expects something in Celsius but is given something in Fahrenheit, the compiler will make the transformation in the background. Let’s say we have a furnace that can add enough heat to a room to raise the temperature by 10℃.

def heatItUp(c: Celsius) = Celsius(c.degrees + 10.0)

If I call my heatItUp but give it the room temperature in Fahrenheit…

scala> heatItUp(Fahrenheit(72.0))
res1: Celsius = Celsius(32.22222222222222)

And if I explicitly define a val with type Fahrenheit and assign it the previous value…

scala> val warmerRoom: Fahrenheit = res1
warmerRoom: Fahrenheit = Fahrenheit(90.0)

The conversion is again done for me automagically in the background! Now honestly, it would be more explicit to say something like this:

scala> c2f(heatItUp(Fahrenheit(72.0)))
res2: Fahrenheit = Fahrenheit(90.0)

And indeed, if you have some code where you know you’re mixing Celsius and Fahrenheit, it’s probably best to be explicit so that someone else reading your code isn’t surprised by this transparent conversion-on-the-fly.

But that’s it: you now know how to write implicit conversions. The last thing I would just say is that the user of these conversions needs to import that implicit definition so it’s in scope. What you might do is put the conversion in the companion object like this:

case class Fahrenheit(degrees: Double)
object Fahrenheit {
implicit def f2c(f: Fahrenheit): Celsius =
Celsius((f.degrees - 32.0) * (5.0 / 9.0))
}

And then someone wanting to leverage your magical implicit conversion would have to import Fahrenheit._ in his or her codebase.

How to do this in Scala 3

For Scala 3, the designers created a specific typeclass scala.Conversion to handle this. The usage looks a little like this:

object MyLibrary:
given Conversion[Fahrenheit, Celsius] = f =>
Celsius((f.degrees - 32.0) * (5.0 / 9.0))

But they always wanted to make this almost hard to use to discourage bad patterns, so to use this conversion, you have to include both MyLibrary.given (wildcard import i.e. MyLibrary.* won’t work!) and you’ll get a compiler feature warning if you don’t also import scala.language.implicitConversions or set the -language:implicitConversions compiler option.

Coming up next: Implicit Classes

In the second article, I will show you the Second Implicit which will be much more useful. I find it very aesthetically pleasing, and I use it all over in my own codebase.

--

--

Murray Todd Williams
Murray Todd Williams

Written by Murray Todd Williams

Life-long learner, foodie and wine enthusiast, living in Austin, Texas.

No responses yet