Scala Implicits
Examples
Implicit Conversion
An implicit conversion allows the compiler to automatically convert an object of one type to another type. This allows the code to treat an object as an object of another type.
case class Foo(i: Int)
// without the implicit
Foo(40) + 2 // compilation-error (type mismatch)
// defines how to turn a Foo into an Int
implicit def fooToInt(foo: Foo): Int = foo.i
// now the Foo is converted to Int automatically when needed
Foo(40) + 2 // 42
The conversion is one-way: in this case you cannot convert 42 back to Foo(42). To do so, a second implicit conversion must be defined:
implicit def intToFoo(i: Int): Foo = Foo(i)
Note that this is the mechanism by which a float value can be added to an integer value, for instance.
Implicit conversions should be used sparingly because they obfuscate what is happening. It is a best practice to use an explicit conversion via a method call unless there's a tangible readability gain from using an implicit conversion.
There is no significant performance impact of implicit conversions.
Scala automatically imports a variety of implicit conversions in scala.Predef, including all conversions from Java to Scala and back. These are included by default in any file compilation.
Implicit Parameters
Implicit parameters can be useful if a parameter of a type should be defined once in the scope and then applied to all functions that use a value of that type.
A normal function call looks something like this:
// import the duration methods
import scala.concurrent.duration._
// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis
val timeout = 1.second
// timeout: scala.concurrent.duration.FiniteDuration = 1 second
// to call it
doLongRunningTask(timeout) // 1000
Now lets say we have some methods that all have a timeout duration, and we want to call all those methods using the same timeout. We can define timeout as an implicit variable.
// import the duration methods
import scala.concurrent.duration._
// dummy methods that use the implicit parameter
def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis
def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis
// we define the value timeout as implicit
implicit val timeout: FiniteDuration = 1.second
// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000
The way this works is that the scalac compiler looks for a value in the scope which is marked as implicit and whose type matches the one of the implicit parameter. If it finds one, it will apply it as the implicit parameter.
Note that this won't work if you define two or even more implicits of the same type in the scope.