Written originally at: The — I didn’t know that — blog
Implicits are used for various critical purposes in Scala.
In English, an implicit is something that suggested or implied though not directly expressed; and as most programming languages — that derive concepts from life — Scala too professes the same use cases for the use of the keyword implicit.
What if we didn’t have to repeatedly pass variables to functions? What if the function could just understand based on the context? Implicit parameters allow you do this.
Let’s take a look at this function that formats a letter based on the parameters passed to it.
Wouldn’t it be great if the function just knew who was writing the letter implicitly ?
Scala supports multiple parameter list and the implicit prefix in function definitions, such that we can rewrite the above function to:
The implicit parameters to the function call are looked up by type of the parameter. The compiler looks for suitable variables that are defined as implicit in the scope of the function call. This also means that only one implicit should be defined for a particular type in that scope.
With implicits there are two scenarios for errors when no variable is explicitly mentioned in the function call:
- There are no implicit variables in the scope OR there are no appropriate implicit variables with the suitable type.
- There are more than one appropriate implicit variable which makes it confusing to the compiler.
Implicit parameters are useful to avoid a lot of boilerplate parameter passing and can make your code more readable. So if you see yourself passing the same parameters several times in quick succession, then you could use implicit to reduce the verbosity and replications.
The above examples are just contrived for an explanation, those are definitely not how implicits should be used.
Implicit Functions — Type Conversions
We can also define an implicit function that the compiler uses when it feels it is appropriate and intended. When the compiler notices that a type is not what is expected in the execution context it searches for an implicit function that would allow it to be converted to the expected type. The Scala library uses this extensively
Let’s dive into some code to understand better:
Now very obviously here the Ints are being implicitly converted to Double. How is Scala doing this?
Below is a Snippet from the Scala Library that defines implicits in the
More specifically in the Companion Object of the Int Class.
Oh, that’s right! Scala has inbuilt implicit functions defined for each of its type that allow the programmer to make use of the implicit conversions. That’s how the Int parameters I passed into the function call were implicitly converted into Double.
Here we notice that there is no implicit conversion from Int to String. Now let’s test what happens when we define a function that expects a String but receives and Int and how we can define our implicit function to enable this.
So an implicit function helps type conversion by converting from Type A to any Type B. There are no constraints to what B can be!
So here, I’ve converted my Cat into a Dog, through the magic of Scala Implicit Functions! Also here’s some context.
This is how implicit conversions are performed in Scala. The implicit function name is not as important as the signature itself. When encountering type mismatch in function calls, the compiler looks for implicit functions that would be suitable in the execution context. In our case, the compiler was looking for an implicit function with the type
Int => String or
Dog => Cat.
Given the knowledge of implicit parameters and implicit functions, the natural flow next would be to create a library of sorts for operations such as conversions and formatting, etc.
Let’s create a case class for String Enhancements that we will call SuperStrings, and we will provide it with the new capabilities.
What we did here is create a type and provide it with some capabilities. Then we created an implicit function to do the conversions and make use of those capabilities. The same can be done with Scala Implicit Classes. They are made for this very purpose and remove the boilerplate around adding the implicit conversions and get the same functionality in lesser lines of code.
Implicit classes that were introduced Scala 2.10 do have some limitations:
- They must be placed inside a class, object or trait.
- They cannot be defined as a case class.
- The constructor of such a class can have only one parameter.
(That being said, in cases where more than one parameter could be needed we could use Tuples.)
Implicit Object — Type Classes
What is a type class and how is it used?
Implicit Objects allow you to implement something called Ad-hoc Polymorphism. Ad-hoc polymorphism to put in simple terms is like function overloading, it allows a polymorphic function to be applied to arguments of different types. It side-tracks from typical polymorphism(
extends) by allowing you to create common behavior for classes.
One of the most common type classes known in Scala is Ordering. (
scala.math.Ordering) It is used for comparing any two instances of the same type thereby enabling other functions sorting and searching. Its implementation in Scala would look something like (Reduced in verbosity and detail)
In Scala, the
sorted function on
SeqLike instances has the following definition:
And hence it can be used as such:
That’s how Scala uses type classes, and the benefit is pretty clear.
We too can make type classes to add behaviors to existing data types and this works especially well since we don’t have access to or even want to directly modify the source code of these data types. Scala Cats makes use of type class concept profusely.
So let’s dive right into it.
How to make your Type Class
So essentially a type class will define behavior for a particular type.
Type classes consist of three components:
- The type class itself, usually defined as a trait with at least one generic type.
- Implementations of instances for the type class corresponding to the type for which it provides behavior.
- The interface(API) with which consumers can use it.
Let’s build again upon the examples of cats and dogs as mentioned in earlier sections.
Now here we can assume that all behaviors that are native to cats and dogs are implemented into the class. (eg: eat, drink, sleep, purr/bark, etc)
I — The Type Class
Let’s define these creative behaviors that pets have begun to show, So I will create a trait called BehaveCreatively and provide the function signatures.
II — Instances of the Type Class
Let’s implement the type classes and provide implementations for these functions. Since only the cats and dogs have started to show these abilities we can only implement for those Pets.
Here, the object the type classes are wrapped in is not important but it’s a good idea to keep them organised.
Making the instance implicit allows me to use them better in the upcoming steps.
III — Interface for Consumers
Now there are two recommended approaches to define interfaces for type classes (mentioned in Scala With Cats) :
- The Interface Object
- The Interface Syntax
III A — Interface Object
This approach uses the functions in objects approach. Generally like it is done in every Utils object.
Now as a consumer of this API, I would have to use it as such:
This approach provides you with a new function which you can apply to your creative Dogs and Cats. But this also seems like too much work to create a “Utils” function for these Pets. I prefer the next approach.
III B — Interface Syntax
In this approach, after creating the type class instances you have to create the implicit class/object.
Now as a consumer of this API, I would have to use it as such:
That’s it. You have provided an implicit conversion from Dog/Cat to BehaveCreativelyOps, and hence as seen above, Dog/Cat can call those functions.
If you assume that you had no access to the original implementation of those data types, this is amazing. You just added functionality to closed class. This provides an approach that lets you add new behavior to existing classes without using traditional inheritance, especially in the case where you can’t (or don’t want to) modify the existing source code of existing data types.
Scala Implicits are valuable and complex features of the language. It’s always good to dig into the languages source code and try to find such interesting features. Having said this, the non-explicit nature of implicits makes it easy to go wrong with it. They are usually used for very specific purposes and overuse of it is generally not recommended.