Scala implicits: Presentations

Apiumhub - Apr 25 '19 - - Dev Community

This article is not going to be an in-depth look at the Scala implicits, it will be about what they are. This article is for those who start learning Scala and need a first-look to the implicits as well for those who despite not using Scala want to know what “implicits” are about.

It will deal with a series of articles, this being the first (and serving as an index for the upcoming ones) and continuing with more detailed and in-depth articles on Scala implicits; their operation and their use.

So, let’s start:

What does implicit and explicit mean?

These two words are used very often when we speak about Scala implicits ( oh, well, I did not expect it) and like almost everything in the world of programming, they are borrowed from other aspects of our lives.

The dictionary definition is the following:

  • implicit, ta Del lat. implicĭtus. adj Included in something else without expressing it.
  • Explicit, ta Del lat. explicĭtus. adj That expresses one thing clearly and determinedly.

But let’s look at an everyday example. When we speak with each other, we do not explicitly mention everything we talk about, but there are many things that are understood by context.

If, for example, we are going to go out on a motorcycle and I ask you to give me the helmet, you will give me my helmet, however, I have not explicitly said that it is that helmet. You have understood it by context, that when I asked for the helmet I was referring to mine, it was implicit.

In fact, it is weird to mention explicitly everything that we refer to * ( except a purely technical context in which precise instructions are given) *.

And in the code?

Okay, but what does it have to do with programming?

Well, Scala has a concept that is similar to the one we just described.

What would happen if we did not have to explicitly pass parameters to a function? If the function understood them by context? Or if we did not have to call a function explicitly and the compiler understood that by the context in which we are? Would we want to use it? That’s the concept, everything is in the context, and there are different ways in which Scala has implemented the concept of implicits.

What types of Scala implicits are there?

implicit parameters.

The methods in Scala can receive a last list of parameters, with the prefix implicit.

def sendText(body: String)(implicit from: String): String = s"$body, from: $from"

This list of parameters can be called normally if you want to:

sendText("hola mundo")("Apiumhub")
//res3: String = hola mundo, from: Apiumhub

But its main characteristic is that you can define an implicit value / function / definition in your code and if it is in the same context … the compiler will use it!

implicit val sender: String = "Alice"
sendText("hola mundo")
//res4: String = hola mundo, from: Alice

implicit def sender: String = "Bob"
sendText("I love you")
//res0: String = I love you, from: Bob

However, if the compiler does not find any implicit value with the indicated type … it will fail:

sendText("hola")
//:13: error: could not find implicit value for parameter from: String
// sendText("hola")
// ^

This allows to eliminate code duplication in calls to different methods that require the same parameter as well as inject collaborators to components (Dependency Injection).

Implicit conversions (implicit functions)

What happens when a method requires a type A and you want to pass it a value of type B? What happens when a method returns a type C and you want a type D? The answer to both questions is the same: Either ask someone to change the signature and the implementation … or you’ll change function A => B to fix the problem. The “problem” is that you will have to apply this function in all the places where you need it, which implies some duplication of code. Moreover, we have to modify our code to add this transformation. And if we are using generics? We could not know what converter to use … maybe using pattern matching …

object Playground {

  def createNumber: Int = scala.util.Random.nextInt

  val myNumber: String = createNumber

}

This is one of those cases, the compiler complains about _ createNumber _ because it returns _ Int _ and not _ String _. We are going to create an implicit conversion to do the transformation automatically:

import scala.language.implicitConversions
object Playground {

  implicit def Int2String(number: Int): String = number.toString()

  def createNumber: Int = scala.util.Random.nextInt

  val myNumber: String = createNumber
  val text: String = 123

}

The power of this tool has few limits and has some practical uses such as defining the transformation of a DTO to domain (or vice versa) in an implicit conversion and forget about having to apply any kind of transformation ever, as it will be done automatically .

Implicit classes

The parameters and the implicit transformations are the best known, but there are more types, such as the implicit classes.

object Playground {
    implicit class MyString(x: String) {
        def mirror: String = x.reverse
        def addThings: String = x+"abcdefg"
    }
}

What do you think this piece of code does? The answer is easy (and many will say, aaaah okay) extension methods

import Playground.MyString
"123456".mirror
//res1: String = 654321
"123456".addThings
//res2: String = 123456abcdefg

Like all the implicit ones it has its limitations, but also a lot of utility: How would you add additional behavior to a class, which could or could not be yours? Composition or inheritance, right? And if it is the last one and you can not extend it? You have a composition, and that can mean making a wrapper, etc.

final class Author(private val name: String) {
  def sayname: String = name
}

object Playground {
  implicit class MyAuthor(x: Author) {
    def shoutname: String = x.sayname+"!!!"
  }
}

import Playground.MyAuthor

val author:Author = new Author("Jack")
author.shoutname
//res7: String = Jack!!!

Like this we can work with Author in a natural way.

Implicit objects

This is the most used form of implicit and at the same time the least used by itself. It allows to create and use typeclasses, which are widely used both in the stdlib and in libraries, however their use besides the typeclasses is practically non-existent. An implicit object that the compiler can deliver when an implicit parameter of the same type as that object is requested. It’s like an implicit parameter for objects to understand each other.

object Playground {

  trait MyTrait {
    val number: Int
    def transform(number:Int): String
  }

  implicit object MyObject extends MyTrait {
    val number: Int = 1
    def transform(number:Int): String = number.toString
  }

  def doSomething(implicit theTrait: MyTrait) = theTrait.transform(theTrait.number)

}

import Playground._

doSomething
//res1: String = 1

You may think, that you don’t see a lot of use cases. Well, later you will see what you can get by using these implicit objects.

What problems do they have?

Code Darkness

The implicits are a very powerful tool. You may want to abuse them once you start discovering them. And that’s not good, believe me, I’ve lived it. A code in which the implicits are abused is the most difficult thing to understand, follow and debug that you can find. Everything is magic (black magic in many cases), things happen and at first sight you have no control over it. It is possible that you leave an implicit to declare and that everything compiles, because someone has declared an implicit value in that scope and the types coincide, and believe me, to debug that is criminal.

ambiguous implicit definition

The implicit ones are looked for based on the required type, that means that we can not have two implicits with the same type sharing scope, the compiler would not know what to do!

object Playground {
  implicit val anumber: Int = 1
  def plus1(implicit number: Int): Int = number+1
}

import Playground._
implicit val myNumber: Int = 0
plus1
//:1: error: ambiguous implicit values:
// both value anumber in object Playground of type => Int
// and value myNumber of type => Int
// match expected type Int
// plus1
// ^

Although you want to use them for different things, if they have the same type, you can not have two implicits sharing the same scope. Which brings us to a conclusion: we need to be careful with Primitive Obsession. If we type in a specific way, with specific types and not with primitives, we can avoid this type of problems.

Final Words: Scala implicits

Summing up, we eliminate boilerplate with the implicits, we write much less code and we solve everything in compilation, nevertheless everything is not perfect. Code sometimes can be impossible to understand (The authors of Kotlin have taken the specific decision not to implement them in the language).

We could say: “you have to use them in moderation”, but for us, the question is not so much about moderation or not, but the criteria and the patterns of how and when to use them since this is an architectural decision.

At the end of this series, we will mention some criteria examples.

Recommended readings on implicits

If you want a light reading on implicits, you can read the upcoming chapter in which we will talk about patterns with Scala implicits , how the compiler looks for them and examples found in the stdlib and in bookstores like scalaz. Also, you can read these recommendations:

If you are interested in Scala implicits or in software development in general, I highly recommend you to subscribe to our monthly newsletter!

If you found this article about Scala Implicits interesting, you might like…

Scala generics I: Scala type bounds

Scala generics II: covariance and contravariance

Scala Generics III: generalized type constraints

BDD: user interface testing

F-bound over a generic type in Scala

Microservices vs Monolithic architecture

“Almost-infinit” scalability

The post Scala implicits: Presentations appeared first on Apiumhub.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player