Implied, Lisa, or Implode?

One of the worst fears a Scalaman may have is implicits: you don’t like them, you avoid them, they’re scary, they’re bloody hell.

satan

And it’s a pitty that, being able to use that powerful tool, you might get in panic and forget about it, avoiding its use. Implicits may result a fancy solution for certain circumstances as we will enum you later.

To have a much better understanding, lets set up a theoretical framwork.

Implicit values

Imagine you are defining ‘identity’ function over integer numbers’ operations (given an integer number and an operation, it should return the same number). Depending on operation type, the value to be used in the operation (add,product) is different. For example, for adding we use zero (0 + n = n) and for multiplying we use 1 (1 * n = n). If you want to avoid passing the neutral element in a explicit way, you can use implicit values as follows.

First of all, we declare an implicit integer that will represent adding neutral element:

implicit val addNeutralElement: Int = 0

And now, we can declare identity function at adding:

def addIdentity(n: Int)(implicit neutral: Int): Int =
  n + neutral

Have a closer look and realize that implicit parameters in the method are declared in a different parameter group, and they are preceded by the reserverd word ‘implicit‘.

Another tip to have in mind is that, what really matters is the argument type instead of its name: compiler will look for an implicit integer within the set up scope.

This can be also applied to methods:

implicit def generateAddIdentity():Int = 0

…and objects…

abstract class NeutralElement(n: Int)
implicit case object AddNeutralElement extends NeutralElement(0)

Implicit ambiguity / Scopes

So let’s say we want to define now the identity function for multiplying. At the same scope, we could define another implicit value for product neutral element:

implicit val addNeutralElement: Int = 0
implicit val productNeutralElement: Int = 1
def addIdentity(n: Int)(implicit neutral: Int): Int =
  n + neutral
def productIdentity(n: Int)(implicit neutral: Int): Int =
  n * neutral

If we try to execute any of both methods…Woops! The compiler will complain about something nearly understandable:

scala> addIdentity(2)
<console>:13: error: ambiguous implicit values:
 both value addNeutralElement of type => Int
 and value productNeutralElement of type => Int
 match expected type Int
              addIdentity(2)
                         ^

What it really means is it doesn’t know which of both implicit values is the needed one: there’s implicit ambiguity. To avoid this, you can define different scopes which are provided by context objects. Something like:

object AddStuff {
  implicit val addNeutralElement: Int = 0
  def addIdentity(n: Int)(implicit neutral: Int): Int =
    n + neutral
}

//in your snippet...

{
  import AddStuff._
  addIdentity(2)
}

I know this is yelling: “I need type-classes!” but we’re not removing these gold minutes from implicits topc. We’re not that much cruel…

Implicit classes

We can also define implicit classes in Scala. Their main target is to extend functionality of certain classes. For example, if we’re using some third parties’ framework, and one of its classes looks like this:

class ThirdParties {
  def method1(): Int = ???
  def method2(n:Int): Boolean = ???
}

we cannot modify its source code, but if we want to add some extra methods to ThirdParties’ class, we can define an implicit class:

implicit class ExtraThirdParties(tp: ThirdParties){
  def method3(n: Int): Boolean =
    !tp.method2(n)
  def method4(): Int = ???
  def method5(): Int =
    tp.method1() * 2
}

This way, when we type down a ‘ThirdParties’ value and a method that doesn’t belong to its class, the compiler will look for implicit classes and/or methods that may fit in such signature. So we’ll be able to use, both originaly defined methods and the new ones (those we have just implemented in the implicit class):

val myTp = new ThirdParties
myTp.method1()
myTp.method2(5)
myTp.method3(2)
myTp.method4()
myTp.method5()

Use cases

So the million dollar question is, in which cases we can use this powerful swiss knife?

* Setting up execution contexts
* Providing additional information about parameter types (type classes)
* Generating DSLs
* Extending a class functionality (implicit classes)

Providing an example of all these cases would be really tough, so we will take a look at some of them in future posts.

giphy

Peace out! 🙂

Anuncios

3 thoughts on “Implied, Lisa, or Implode?

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s