Mike Slinn

Implicit Conversions

— Draft —

Published 2013-09-18. Last modified 2016-10-26.
Time to read: 6 minutes.

Implicit conversions provide extra functionality to value classes.

The sample code for this lecture can be found in courseNotes/src/main/scala/ImplicitConversions.scala.

Implicit conversion methods are automatically lifted into functions

Implicit methods allow an instance of a type to be viewed as if it were another type. Implicit methods provide implicit conversions.

Using the example from the previous lecture, we can write implicit converters between Int and Multiplier3, and Int and Divider3.

Scala code
@implicitNotFound("Cannot find implicit of type Multiplier3 in scope")
case class Multiplier3(value: Int) extends AnyVal
@implicitNotFound("Cannot find implicit of type Divider3 in scope") case class Divider3(value: Int) extends AnyVal
object ImplicitConversions extends App { implicit val defaultMultiplier = Multiplier3(2)
implicit val defaultDivider = Divider3(3)
def multiply(value: Int)(implicit multiplier: Multiplier3): Int = value * multiplier.value
def divide(value: Int)(implicit divider: Divider3): Int = value / divider.value
implicit def intToMultiplier(int: Int) = Multiplier3(int)
implicit def intToDivider(int: Int) = Divider3(int)
println(s"multiply(2)(3)=${multiply(2)(3)}") println(s"multiply(5)=${multiply(5)}") println(s"divide(12)(4)=${divide(12)(4)}") println(s"divide(9)=${divide(9)}") }

intToMultiplier and intToDivider are implicitly called when the Int values highlighted in orange (3 and 4) need to be converted into Multiplier3 or Divider3 instances. Because these methods are called implicitly, their names do not appear anywhere in the program except where they are defined. Note that the original Int values (3 and 4) are not actually changed; instead, the implicit methods generate new Multiplier3 or Divider3 values from the original Int values, and the new values are passed as parameters to the multiply or divide methods.

Output is:

Output
multiply(2)(3)=6
multiply(5)=10
divide(12)(4)=3
divide(9)=3

You can run the code as follows:

Shell
$ sbt "run-main ImplicitConversions"

Implicit methods can be defined in classes, objects and traits.

Implicit Resolution Rules

The previous lecture described where the Scala compiler looks for implicit values. The compiler uses the following rules to resolve implicit values and functions.

  1. Decoration – Properties and methods must be decorated with implicit in order to be considered for resolution by the compiler.
  2. Scope – The compiler only considers the implicits currently in scope as a single identifier for resolution. Properties of an implicit object are not considered, unless they are also marked as an implicit.
    Scala code
    implicit object Blah {
      implicit val yesMe: Boolean = true
      val notMe: Boolean = false
    }
  3. One at a time – Only one implicit is tried at a time to resolve - implicits do not normally chain.
  4. Explicits first – if a value or function is explicitly provided, then implicits are not used.
  5. Only One in Scope - If there are two applicable implicits in the same scope, the compiler will issue an error rather than pick one at random.

Implicit Scope Management

It is often helpful to define implicits in a package object. We discussed package objects in the Scala Imports and Packages lecture of the Introduction to Scala course. This makes accessing the implicit easier. The code below uses a trick to define the implicit converter stringToYeller in the yeller package object.

Scala code
package yeller {
  case class Yeller(s: String) {
    def yell: String = s.toUpperCase + "!!"
  }
object `package` { implicit def stringToYeller(s: String): Yeller = Yeller(s) } }
object YellerMain extends App { import yeller._
println("Look out".yell) }

Notice the backticks that surround the word `package`. This causes the word package to be considered as an object name instead of a Scala keyword. You can place backticks around otherwise illegal package and variable names to force them to be evaluated as names. The result is that stringToYeller is defined in the yeller package object, which is imported into YellerMain by import yeller._. Because the implicit converter stringToYeller is now in scope, strings in the YellerMain object that have an otherwise illegal method invoked are automatically converted to Yeller instances as required. This allows us to write "Look out".yell.

You can run this program as follows:

Shell
$ sbt "runMain YellerMain"
LOOK OUT!! 

Exercise

  1. Rewrite YellerMain so that it is more efficient by using value classes.
  2. Add a whisper method that forces Strings to lower case, and prefaces them with "Shhh! "

Solution

This solution is provided as solutions.EfficientYeller.

Scala code
package solutions
case class Yeller(val s: String) extends AnyVal { def yell: String = s.toUpperCase + "!!" def whisper: String = "Shhh! " + s.toLowerCase }
object `package` { implicit def stringToYeller(s: String): Yeller = Yeller(s) }
object EfficientYeller extends App { println("Look out".yell) println("This is a secret".whisper) }

You can run this solution by typing:

Scala REPL
scala> sbt "runMain solutions.EfficientYeller"
LOOK OUT!!
Shhh! this is a secret 

Default Values for Implicit Parameters

You can specify default values for implicit parameters. This can be a source of difficult-to-find bugs. I recommend that you do not do this.

Scala REPL
scala> def asdf(implicit x: Int=3): Unit = println(x)
asdf: (implicit x: Int)Unit
scala>
implicit val y=2 y: Int = 2
scala>
asdf() 3

You can run this code as follows.

Shell
$ sbt "runMain ImplicitDefaultValues"

Coercion by Implicit Conversions

As you know, coercion is what a compiler of a typed language does when converting an argument or an operand to the type expected by a function or an operator. As we have already seen, Scala supports conversion through implicits. Here is an example:

In the Either, Left and Right lecture of the Introduction to Scala course we defined an Either[NonLivingThing, LivingThing] and assigned it a value, like this:

Scala REPL
scala> case class LivingThing(name: String, species: String)
defined class LivingThing
scala>
case class NonLivingThing(name: String, description: String) defined class NonLivingThing
scala>
val thing1: Either[NonLivingThing, LivingThing] = Right(LivingThing("Leatherback Turtle", "Dermochelys coriacea")) thing1: Either[NonLivingThing,LivingThing] = Right(LivingThing(Leatherback Turtle,Dermochelys coriacea))

In that lecture I mentioned that the Scala compiler is not smart enough to figure out that a LivingThing belongs on the Right.

Scala REPL
scala> val thing1: Either[NonLivingThing, LivingThing] = LivingThing("Leatherback Turtle", "Dermochelys coriacea")
<console>:15: error: type mismatch;
 found   : LivingThing
 required: Either[NonLivingThing,LivingThing]
       val thing1: Either[NonLivingThing, LivingThing] = LivingThing("Leatherback Turtle", "Dermochelys coriacea") 

We could define an implicit conversion to simplify our code, however.

Scala REPL
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala>
implicit def livingToRight(thing: LivingThing): Right[NonLivingThing, LivingThing] = Right(thing) livingToRight: (thing: LivingThing)Right[NonLivingThing,LivingThing]

Now the error message goes away:

Scala REPL
scala> val thing1: Either[NonLivingThing, LivingThing] = LivingThing("Leatherback Turtle", "Dermochelys coriacea")
thing1: Either[NonLivingThing,LivingThing] = Right(LivingThing(Leatherback Turtle,Dermochelys coriacea)) 

Another Example

Scala code
case class Complex(re: Double, im: Double) {
  def +(that: Complex): Complex = Complex(re + that.re, im + that.im)
def -(that: Complex): Complex = Complex(re - that.re, im - that.im)
override def toString = s"$re + ${im}i" }
implicit def doubleToComplex(d: Double): Complex = Complex(d, 0)

Let’s implicitly use the method doubleToComplex to convert a Double instance into a Complex instance, so we can add it to another Complex instance.

Scala REPL
scala> Complex(2.0, 5.0) + 5.0
res8: Complex = 7.0 + 5.0i
scala>
5.0 + Complex(1.0, -2.0) res9: Complex = 6.0 + -2.0i

You can run this code as follows:

Shell
$ sbt "runMain ImplicitCoercion"

If you view line 24 of ImplicitCoercion.scala in IntelliJ IDEA, you will notice that the number 5.0 is underlined in gray. IntelliJ IDEA underlines values and variables in gray that are subject to implicit conversions. This is the line:
println(s"""Complex(2, 5) + 5.0 = ${Complex(2, 5) + 5.0}""")

Click on 5.0, then press Ctrl-Shift-Q. This shows the implicits that are in scope which are candidates for conversion, and highlights the best match used by the compiler.

This means the occurrence of 5.0 on line 24 is implicitly converted to Complex(5.0, 0.0) by doubleToComplex.

Exercise – Convert Tuples to Complex

Extend the ImplicitCoercion program so Tuple2s of Ints, Floats, Doubles and all 9 combinations thereof can be added and subtracted with Complex instances. Assume that the first property of a tuple is the real part, and the second property is the imaginary part.

Hint

As discussed in the previous course, you could use the special notation for Tuple2. However, your program will read better if you do not.

Solution

Scala code
package solutions
object ImplicitCoercion extends App { case class Complex(re: Double, im: Double) { def +(that: Complex): Complex = Complex(re + that.re, im + that.im)
def -(that: Complex): Complex = Complex(this.re - that.re, this.im - that.im)
override def toString = s"$re + ${im}i" }
implicit def doubleToComplex(d: Double): Complex = Complex(d, 0)
implicit def tupleToComplex1(t: (Int, Int)) = Complex(t._1, t._2) implicit def tupleToComplex2(t: (Int, Float)) = Complex(t._1, t._2) implicit def tupleToComplex3(t: (Float, Int)) = Complex(t._1, t._2) implicit def tupleToComplex4(t: (Float, Float)) = Complex(t._1, t._2) implicit def tupleToComplex5(t: (Int, Double)) = Complex(t._1, t._2) implicit def tupleToComplex6(t: (Double, Int)) = Complex(t._1, t._2) implicit def tupleToComplex7(t: (Double, Double)) = Complex(t._1, t._2) implicit def tupleToComplex8(t: (Float, Double)) = Complex(t._1, t._2) implicit def tupleToComplex9(t: (Double, Float)) = Complex(t._1, t._2)
println(s"""Complex(2, 5) + 5.0 = ${Complex(2, 5) + 5.0}""") println(s"""5.0 + Complex(1, -2) = ${5.0 + Complex(1, -2)}""")
println(s"""Complex(2, 5) + (2f, 5) = ${Complex(2, 5) + (2f, 5)}""") println(s"""Complex(2, 5) + (2, 5f) = ${Complex(2, 5) + (2, 5f)}""") println(s"""Complex(2, 5) + (2f, 5f) = ${Complex(2, 5) + (2f, 5f)}""")
println(s"""Complex(2, 5) + (2d, 5) = ${Complex(2, 5) + (2d, 5)}""") println(s"""Complex(2, 5) + (2, 5d) = ${Complex(2, 5) + (2, 5d)}""") println(s"""Complex(2, 5) + (2d, 5d) = ${Complex(2, 5) + (2d, 5d)}""")
println(s"""Complex(2, 5) + (2f, 5d) = ${Complex(2, 5) + (2f, 5d)}""") println(s"""Complex(2, 5) + (2d, 5f) = ${Complex(2, 5) + (2d, 5f)}""") println(s"""Complex(2, 5) + (2d, 5d) = ${Complex(2, 5) + (2d, 5d)}""") }

You can run this solution by typing:

Shell
$ sbt "runMain solutions.ImplicitCoercion"
Complex(2, 5) + 5.0 = 7.0 + 5.0i
5.0 + Complex(1, -2) = 6.0 + -2.0i
Complex(2, 5) + (2f, 5) = 4.0 + 10.0i
Complex(2, 5) + (2, 5f) = 4.0 + 10.0i
Complex(2, 5) + (2f, 5f) = 4.0 + 10.0i
Complex(2, 5) + (2d, 5) = 4.0 + 10.0i
Complex(2, 5) + (2, 5d) = 4.0 + 10.0i
Complex(2, 5) + (2d, 5d) = 4.0 + 10.0i
Complex(2, 5) + (2f, 5d) = 4.0 + 10.0i
Complex(2, 5) + (2d, 5f) = 4.0 + 10.0i
Complex(2, 5) + (2d, 5d) = 4.0 + 10.0i 

Implicit Search Order

This section contains a more detailed explanation of where the Scala compiler looks for implicit values and converters.

  1. Implicits defined in the current scope bind first. The package object is part of the current scope.
    Scala code
    object InnerScope {
      implicit val list2 = List(1, 2, 3)
    def res(implicit list: List[Int]): List[Int] = list }
    object OuterScope extends App { implicit val list = List(1, 2)
    println(InnerScope.res) }

    To run this program, type:

    Shell
    $ sbt "runMain OuterScope"
    List(1, 2) 
  2. If an implicit of the required type is not found in the current scope or parent scope, the Scala compiler then searches imports for an implicit declaration of the required type.
    Scala code
    object InnerScope {
      implicit val list2 = List(1, 2, 3)
    def res(implicit list: List[Int]): List[Int] = list }
    object ImportedImplicit extends App { import InnerScope._
    println(res) }

    To run this program, type:

    Shell
    $ sbt "runMain ImportedImplicit"
    List(1, 2, 3) 
  3. The Scala compiler then looks in companion objects for implicits:
    Shell
    object CompanionScope extends App {
      class A(val n: Int) {
        def +(other: A) = new A(n + other.n)
      }
    object A { implicit def fromInt(n: Int): A = new A(n) }
    val x = 1 + new A(1) // is converted into: val y = A.fromInt(1) + new A(1)
    println(s"x.n=${x.n}") println(s"y.n=${y.n}") }

    To run this, type:

    Shell
    $ sbt "runMain CompanionScope"
    x.n=2
    y.n=2 

Here is an excellent article on the subject. I provide a modified version of the code in ImplicitFun.scala in the courseNotes project.

Predef.scala

The source code for the Scala runtime library includes Predef.scala, which defines type aliases and many implicit conversion methods. These definitions are automatically imported into every Scala program. Predef provides many implicit conversions which convert from a value type, such as Int or String, to an enhancing type, such as RichInt and WrappedString.

Predef.scala also defines a useful method called require, which has the following signature:

Scala code
final def require(requirement: Boolean, message: => Any): Unit

require tests an expression, throwing an IllegalArgumentException if false. This method is similar to assert, but blames the caller of the method for violating the condition.

I encourage you to read the source code for Predef.scala.

REPL :implicits

The Scala REPL’s :implicits command displays all the implicits in scope. Unless you add the -v switch, it does not display the implicit conversions defined in Predef.scala.

Scala REPL
scala> :implicits
No implicits have been imported other than those in Predef.
scala>
:implicits -v /* 64 implicit members imported from scala.Predef */ /* 6 inherited from scala */ final implicit class ArrayCharSequence extends final implicit class ArrowAssoc extends final implicit class Ensuring extends final implicit class SeqCharSequence extends final implicit class StringFormat extends final implicit class any2stringadd extends
/* 37 inherited from scala.Predef */ final implicit def ArrowAssoc[A](self: A): ArrowAssoc[A] final implicit def Ensuring[A](self: A): Ensuring[A] final implicit def StringFormat[A](self: A): StringFormat[A] final implicit def any2stringadd[A](self: A): any2stringadd[A] implicit def booleanArrayOps(xs: Array[Boolean]): scala.collection.ArrayOps[Boolean] implicit def byteArrayOps(xs: Array[Byte]): scala.collection.ArrayOps[Byte] implicit def charArrayOps(xs: Array[Char]): scala.collection.ArrayOps[Char] implicit def doubleArrayOps(xs: Array[Double]): scala.collection.ArrayOps[Double] implicit def floatArrayOps(xs: Array[Float]): scala.collection.ArrayOps[Float] implicit def genericArrayOps[T](xs: Array[T]): scala.collection.ArrayOps[T] implicit def intArrayOps(xs: Array[Int]): scala.collection.ArrayOps[Int] implicit def longArrayOps(xs: Array[Long]): scala.collection.ArrayOps[Long] implicit def refArrayOps[T <: AnyRef](xs: Array[T]): scala.collection.ArrayOps[T] implicit def shortArrayOps(xs: Array[Short]): scala.collection.ArrayOps[Short] implicit def unitArrayOps(xs: Array[Unit]): scala.collection.ArrayOps[Unit]
implicit def $conforms[A]: A => A final implicit def ArrayCharSequence(arrayOfChars: Array[Char]): ArrayCharSequence implicit def Boolean2boolean(x: Boolean): Boolean implicit def Byte2byte(x: Byte): Byte implicit def Character2char(x: Character): Char implicit def Double2double(x: Double): Double implicit def Float2float(x: Float): Float implicit def Integer2int(x: Integer): Int implicit def Long2long(x: Long): Long final implicit def SeqCharSequence(sequenceOfChars: scala.collection.IndexedSeq[Char]): SeqCharSequence implicit def Short2short(x: Short): Short implicit def augmentString(x: String): scala.collection.StringOps implicit def boolean2Boolean(x: Boolean): Boolean implicit def byte2Byte(x: Byte): Byte implicit def char2Character(x: Char): Character implicit def double2Double(x: Double): Double implicit def float2Float(x: Float): Float implicit def int2Integer(x: Int): Integer implicit def long2Long(x: Long): Long implicit def short2Short(x: Short): Short implicit def tuple2ToZippedOps[T1, T2](x: (T1, T2)): runtime.Tuple2Zipped.Ops[T1,T2] implicit def tuple3ToZippedOps[T1, T2, T3](x: (T1, T2, T3)): runtime.Tuple3Zipped.Ops[T1,T2,T3]
/* 20 inherited from scala.LowPriorityImplicits */ implicit def booleanWrapper(x: Boolean): runtime.RichBoolean implicit def byteWrapper(x: Byte): runtime.RichByte implicit def charWrapper(c: Char): runtime.RichChar implicit def doubleWrapper(x: Double): runtime.RichDouble implicit def floatWrapper(x: Float): runtime.RichFloat implicit def genericWrapArray[T](xs: Array[T]): mutable.ArraySeq[T] implicit def intWrapper(x: Int): runtime.RichInt implicit def longWrapper(x: Long): runtime.RichLong implicit def shortWrapper(x: Short): runtime.RichShort implicit def wrapBooleanArray(xs: Array[Boolean]): mutable.ArraySeq.ofBoolean implicit def wrapByteArray(xs: Array[Byte]): mutable.ArraySeq.ofByte implicit def wrapCharArray(xs: Array[Char]): mutable.ArraySeq.ofChar implicit def wrapDoubleArray(xs: Array[Double]): mutable.ArraySeq.ofDouble implicit def wrapFloatArray(xs: Array[Float]): mutable.ArraySeq.ofFloat implicit def wrapIntArray(xs: Array[Int]): mutable.ArraySeq.ofInt implicit def wrapLongArray(xs: Array[Long]): mutable.ArraySeq.ofLong implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): mutable.ArraySeq.ofRef[T] implicit def wrapShortArray(xs: Array[Short]): mutable.ArraySeq.ofShort implicit def wrapString(s: String): immutable.WrappedString implicit def wrapUnitArray(xs: Array[Unit]): mutable.ArraySeq.ofUnit
/* 1 inherited from scala.LowPriorityImplicits2 */ implicit def copyArrayToImmutableIndexedSeq[T](xs: Array[T]): IndexedSeq[T]

Remember that all of the implicit conversions are defined as methods, but Scala lifts all of them into functions. (We discussed method ’lifting’ in the Introduction to Scala course.) This is only important when you want to use implicitly to discover the implicit converter in scope.

Discovering the Current Implicit Conversion In Scope with implicitly

Sometimes you need to access the implicit function that converts between two types. The implicitly method, defined in Predef, can check if an implicit converter of a given type is available and return it if so.

For example, Int can be implicitly converted to Long, as you can see in the type hierarchy diagram for Int (click on "Type Hierarchy").

We can use the implicitly method in the REPL to discover the function that performs the implicit conversion from Int to Long.

Scala REPL
scala> val i2l = implicitly[Function[Int, Long]]
i2l: Int => Long = <function1>
scala>
i2l(3) res10: Long = 3

We can use an alternative syntax to obtain the same implicit conversion function, and then its apply method is invoked while passing in 5, so the result 5L can be computed.

Scala REPL
scala> implicitly[Int => Long].apply(5)
res11: Long = 5 

This code can be run as follows:

Shell
$ sbt "runMain ImplicitlyConversion"

Methods as Implicit Values

This technique provides controlled conversions between types where the values are all implicitly known.

This example uses the Multipler class and the multiply method from the Implicit Values lecture. Here they are again for your convenience:

Scala REPL
scala> case class Multiplier(value: Int)
defined class Multiplier
scala> %}def multiply(value: Int)(implicit multiplier: Multiplier): Int = value * multiplier.value multiply: (value: Int)(implicit multiplier: Multiplier)Int

A method may be used as an implicit argument, however it must not take any arguments itself, unless those arguments are implicit. That’s because when the implicit method is used and substituted as the missing parameter, there is no way to pass parameters to it, unless those parameters can themselves be found implicitly.

To see this in action, let’s introduce a Factor class and an implicit conversion from Factor to Multiplier called factorToMultiplier that accepts an implicit Factor.

Scala REPL
scala> case class Factor(value: Int)
defined class Factor
scala>
implicit def factorToMultiplier(implicit factor: Factor): Multiplier = Multiplier(factor.value) factorToInt: (implicit factor: Factor)Int

Now we’ll create an implicit instance of Factor.

Scala REPL
scala> implicit val defaultFactor = Factor(3)
defaultFactor: Factor = Factor(3) 

The test method shown next is just to demonstrate that if a method is invoked that accepts an implicit parameter, but no value is supplied for that parameter, and there is no implicit value of exactly the right type in scope (because there is no implicit Multiplier in scope), the Scala compiler will search for another implicit value that can be converted to the desired type of the implicit parameter. In this case, the implicit defaultFactor can implicitly be converted to a Multiplier by the implicit factorToMultiplier method that is in scope.

Scala REPL
scala> def test(implicit m: Multiplier) = println(m)
test: (implicit m: Multiplier)Unit
scala>
test Multiplier(3)

The same conversion is performed for the multiply method’s second (implicit) parameter list. We implicitly invoke the factorToMultiplier method, which causes the Factor instance in scope to be converted to a Multiplier, which is then implicitly passed to the second parameter list of multiply.

Scala REPL
scala> val y = multiply(3)
y: Int = 9 

You can run this code example from the command line by typing:

Shell
$ sbt "runMain MethodsImplicitValues"
... lots of output ...
result=9 

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.