Scala Implicits - Journey Begins
This article covers the 2 basic concepts of Scala Implicits. Implicit parameters and implicit conversions
Before you read
I am assuming that you know basic Scala Programming, Scala Type Bounds (aka Generics)
Implicit definition
Here what Scala docs say
Implicits in Scala refers to either a value that can be passed “automatically”, so to speak, or a conversion from one type to another that is made automatically. That's what Scala exactly does. Scala Docs
Scala has
- implicit parameters
- implicit conversions
The context in which they are used are entirely different. Lets explore each of them in detail
Implicit parameters
Firstly, the implicit parameters Lets start with a simple example. A method that calculates the area of a circle
def areaOfCircle(radius: Double, piValue: Double): Double
= piValue * radius * radius
println(areaOfCircle(5, 3.14))
Let us take another method that calculates the circumference of a circle
def circumferenceOfCircle(radius: Double, piValue: Double): Double
= 2 * piValue * radius
println(circumferenceOfCircle(5, 3.14))
In the above two code snippets you can see that I must pass the radius and the value of pi. Let us send pi Value as an implicit variable. We slightly change the above methods like this
def areaOfCircle(radius: Double)(implicit piValue: Double): Double = piValue * radius * radius
def circumferenceOfCircle(radius: Double)(implicit piValue: Double): Double = 2 * piValue * radius
Now, below is the complete code snippet
object ImplicitParametersExamples extends App {
implicit val approxPiValue: Double = 3.14
def areaOfCircle(radius: Double)(implicit piValue: Double): Double = piValue * radius * radius
def circumferenceOfCircle(radius: Double)(implicit piValue: Double): Double = 2 * piValue * radius
println(areaOfCircle(5))
println(circumferenceOfCircle(10))
}
In the above code snippet, you can see that we declared the piValue as an implicit value. And since both methods expect pi Value as an implicit variable, Scala compiler automatically uses approxPiValue and passes it as piValue while calling these methods.
Remember that Scala will use any double value that is in its scope as an implicit value. And in this case it is approxPiValue
Also, in the above example, I can replace approxPiValue with an implicit method that returns double value
implicit def calculatePIValue: Double = 22d/7d
Scala compiler will now resolve piValue from the above value returned by calculatePIValue.
How does Scala Compiler resolve an implicit parameter?
Now, the question is, where does Scala look for implicit parameters? In our above example, there is no ambiguity since we defined the implicit value in the same method. But in practicality it does not make sense defining implicit values in the same method.
Scala compiler tries to find the value in lexical scope failing which it tries next on the implicit scope
Lexical scope
Scala compiler will check for a implicit value of the same type
- In the same class or object
- You can also bring implicit values defined in an Object or a trait using import statement
Implicit scope
Scala compiler will next search for implicit values defined in the companion object
What will happen when Scala compiler finds multiple values in lexical or implicit scope? Well, it uses overload resolution algorithm to pick one of them. If the compiler is unable to determine the implicit value, an error is thrown. Let me keep the article simple and assume that compiler finds an implicit value in either of the scopes
We covered implicit parameters because it's the simplest form of implicit. Let us now get into implicit conversions
Implicit conversions
Let us take a different example. Lets say that I want to code a method called vulcanGreeting. This vulcanGreeting will take an argument name and prints the greeting message. Something simple like this,
object VulcanGreet extends App {
def sayHello(name: String): Unit = println(s"Live long and prosper.... ${name}")
sayHello("Kirk")
}
Let's do the above example differently. Lets use implicit conversion
object Greetings {
implicit class VulcanGreeting(name: String) {
def greetInVulcan = println(s"Live long and prosper... ${name}")
}
}
import Greetings._
object GreetingsApp extends App {
"Kirk".greetInVulcan
}
Both the above examples give the same output on the console
Live long and prosper... Kirk
But wait, how am I able to call a method greetInVulcan on a String("Kirk"). There is no such method in String. Welcome to the world of implicit conversions Let's first analyse the below object
object Greetings {
implicit class VulcanGreeting(name: String) {
def greetInVulcan = println(s"Live long and prosper... ${name}")
}
}
The Greetings
object defines an implicit class VulcanGreeting
. This implicit class takes an argument name
. Nothing fancy about this class except for the keyword implicit
Now let's examine the second object
import Greetings._
object GreetingsApp extends App {
"Kirk".greetInVulcan
}
The line import Greetings._
imports all the implicit classes in the object Greetings. Or in other words, the moment you import it, the implicit class VulcanGreeting
comes into the lexical scope.
Now examine the line
"Kirk".greetInVulcan
Since the Scala compiler has VulcanGreeting
in the scope, it converts String
to VulcanGreeting
. And allows you to use all the methods in the VulcanGreeting on the String
Implicit are extensively used in Scala. Understanding implicit is also the first step to understand libraries like Cats and Scalaz
The journey from here
When I started using Cats, I realized the path taken to get a gasp of it has been long and challenging. Cats is a wonderful collection of functional programming tools. Type classes, the corner stone of Cats, is a programming pattern borrowed from Haskell. The first step in this journey is to know about Scala Implicit. My objective is to simplify this journey to Functional programming excellence