ClassTag, Class and war stories…

Sometimes, when working with Scala, the need of working with type metadata just comes out. Even though macros might seem something too far, theorical and tricky (we’ll speak about them later), sometimes getting some info about runtime classes is more than enough.

ClassTag

You could see a ClassTag as some ‘wrapper’ that Scala adds over java.lang.Class runtime classes.

In order to work with ClassTag we need to import:

import scala.reflect.{ClassTag,classTag}

Whereas the first member imported is the class, the second one is just a method that allows to get implicitly the ClassTag of a certain type. I.e.:

scala> classTag[Int]
res0: scala.reflect.ClassTag[Int] = Int

Actually, it’s just syntax sugar for:

def classTag[T:ClassTag]: ClassTag[T] = 
  implicitly[ClassTag[T]]

scala> classTag[Int]
res0: scala.reflect.ClassTag[Int] = Int

There are methods (currently deprecated by type checking via TypeTag) like <:< or =:= that allow checking at compile time the type bounds.
For example, this:

def myMethod[T,U](t: T,u: U)(implicit ev: <:<[T,U]):Unit = 
  println(t,u)

allows constraining the relation between T and U types:

scala> myMethod(new Animal,new Car)
<console>:11: error: Cannot prove that Animal <:< Car. myMethod(new Animal,new Car)

scala> myMethod(new Cat,new Animal)
($line15.$read$$iw$$iw$Cat@350aac89,$line14.$read$$iw$$iw$Animal@1c5920df)

Where are my type parameters?

One of the main problems working with ClassTag is the information loss about the parameter types that you class might have. For example, if we ask for the ClassTag of List[Int] we’ll get that:

scala> println(scala.reflect.classTag[List[Int]])
scala.collection.immutable.List

As you can see, information about Int that parameterizes the list is lost. This process that removes parameter types info at runtime is well known as type erasure.

62517760

Nevertheless, if we need info about the static type, we can use TypeTags (which should be properly explained at future posts, when talking about macros).

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> val tt = typeTag[List[Int]]
tt: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]

Frequent problems

How to get the Class of a known type T?

scala> classOf[Int]
res0: Class[Int] = int

I have a Class, but I need a ClassTag

As easy as:

scala> val clazz = classOf[Int]
clazz: Class[Int] = int

scala> val ctag = ClassTag(clazz)
ctag: scala.reflect.ClassTag[Nothing] = Int

scala> val ctag = scala.reflect.ClassTag[Int](clazz)
ctag: scala.reflect.ClassTag[Int] = Int

Notice that, unless we explicitly point the type, ClassTag builder will infer the most generic possible type (Nothing).

Is T a subtype of U?

We’ve seen before that, at compile time, we can use <:< and so for constraining type bounds but, what if it takes place at runtime? How can we check it?

We can use isAssignableFrom method, from class Class that indicates whether a class is the same class (or superclass) compared to another one:

object A{
  def apply[T:ClassTag] = new {
    def isA[U:ClassTag]: Boolean = 
      classTag[T].runtimeClass.isAssignableFrom(classTag[U].runtimeClass)
  }
}

We could try this code snippet with:

scala> A[Int].isA[String]
res0: Boolean = false

scala> A[String].isA[String]
res1: Boolean = true

Infer the type of an Any

This tip comes out of a question that a colleague made me. ‘Is there any way to, having the folling function:

def isA[T](t: Any): Boolean = ???

check if t value is T typed?’
First obvious step for checking it would be something similar to:

scala> def isA[T](t: Any): Boolean = t.isInstanceOf[T]
<console>:7: warning: abstract type T is unchecked since it is eliminated by erasure
       def isA[T](t: Any): Boolean = t.isInstanceOf[T]
                                                   ^
isA: [T](t: Any)Boolean

Compiler is warning us (Forewarned is forearmed) that we won’t have information about T at runtime, that it’s getting lost. What if we try with different types? It eats them all:

scala> isA[Int]("")
res2: Boolean = true

scala> isA[Int](3)
res3: Boolean = true

In this case, we said ‘okay, what if we just try to check if classes are assignables, getting the info from the ClassTag‘?
It happened that,

scala> val x: Any = 3
x: Any = 3

scala> x.getClass
res2: Class[_] = class java.lang.Integer

scala> classOf[Int].isAssignableFrom(classOf[Int])
res3: Boolean = true

scala> classOf[Int].isAssignableFrom(classOf[java.lang.Integer])
res4: Boolean = false

The x class is java.lang.Integer, which is not assignable from Int class (the same happens with many other primitive types).

So we finally…

…realized that ClassTag has a wonderfull unapply method that allows to get a typed value if this belongs to the ClassTag parameterized class. Just what we needed:

def isA[T:ClassTag](t: Any): Boolean = 
  classTag[T].unapply(t).isDefined

When we tried it at the REPL, we could gladly check that:

scala> val x: Any = 3
x: Any = 3

scala> isA[String](x)
res5: Boolean = false

scala> isA[Int](x)
res6: Boolean = true

Conclusions

It’s not very common to deal with all these isses unless you’re building a generic enough system. However, it doesn’t harm you to have a little idea about what the dark side is about…

137134463824621

Peace out!

Anuncios

ClassTag, Class y batallitas de la mili…

En ocasiones, al trabajar en Scala, surge la necesidad de trabajar con meta-información sobre los tipos. Si bien las macros pueden parecer algo lejano y demasiado tedioso (ya hablaremos de ellas), muchas veces nos basta con poder obtener algo de información sobre las clases de runtime.

ClassTag

Se trata de un ‘envoltorio’ que añade Scala sobre las clases de runtime de java.lang.Class.

Para trabajar con ClassTag debemos importar:

import scala.reflect.{ClassTag,classTag}

Mientras que el primer valor que se importa es la clase, el segundo es un método para obtener implícitamente el ClassTag de un cierto tipo. Por ejemplo:

scala> classTag[Int]
res0: scala.reflect.ClassTag[Int] = Int

En realidad no es más que syntax sugar para lo siguiente:

def classTag[T:ClassTag]: ClassTag[T] = 
  implicitly[ClassTag[T]]

scala> classTag[Int]
res0: scala.reflect.ClassTag[Int] = Int

Disponen de métodos (ahora deprecados por la verificación de tipos mediante TypeTag) como <:< ó =:= que permiten comprobar en tiempo de compilación los bounds de un tipo.
Por ejemplo:

def myMethod[T,U](t: T,u: U)(implicit ev: <:<[T,U]):Unit = 
  println(t,u)

permite restringir la relación entre los tipos T y U:

scala> myMethod(new Animal,new Car)
<console>:11: error: Cannot prove that Animal <:< Car. myMethod(new Animal,new Car) ^ scala> myMethod(new Cat,new Animal)
($line15.$read$$iw$$iw$Cat@350aac89,$line14.$read$$iw$$iw$Animal@1c5920df)

¿Y mis tipos?

Uno de los problemas al trabajar con ClassTag es la pérdida de información sobre los tipos que pueden parametrizar tu clase. Por ejemplo, si preguntamos por el ClassTag de List[Int] obtendremos que:

scala> println(scala.reflect.classTag[List[Int]])
scala.collection.immutable.List

Como podéis ver, se pierde la información sobre el tipo Int que parametriza la lista. Este proceso de borrado de información de tipos en runtime es conocido como type erasure.

62517760

No obstante, en caso de necesitar información estática sobre el tipo, podemos hacer uso de los TypeTag (los cuales introduciremos en futuros posts, cuando hablemos de macros).

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> val tt = typeTag[List[Int]]
tt: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]

Problemas frecuentes

¿Cómo obtener la Class de un tipo T?

scala> classOf[Int]
res0: Class[Int] = int

Tengo un Class, pero necesito un ClassTag

Tan sencillo como

scala> val clazz = classOf[Int]
clazz: Class[Int] = int

scala> val ctag = ClassTag(clazz)
ctag: scala.reflect.ClassTag[Nothing] = Int

scala> val ctag = scala.reflect.ClassTag[Int](clazz)
ctag: scala.reflect.ClassTag[Int] = Int

Fijaos que salvo que indiquemos el tipo de manera explícita, el constructor de ClassTag inferirá el tipo más genérico posible (en este caso Nothing).

¿T es un subtipo de U?

Hemos visto antes, que en tiempo de compilación, podemos usar <:< y similares para restringir los tipos pero, ¿qué ocurre en tiempo de runtime?

Podemos hacer uso del método isAssignableFrom de la case Class que nos indica si una clase es la misma (o superclase) comparado con otra:

object A{
  def apply[T:ClassTag] = new {
    def isA[U:ClassTag]: Boolean = 
      classTag[T].runtimeClass.isAssignableFrom(classTag[U].runtimeClass)
  }
}

Podríamos probar este snippet con:

scala> A[Int].isA[String]
res0: Boolean = false

scala> A[String].isA[String]
res1: Boolean = true

Detectar el tipo a partir de un Any

Este tip surge por una duda que me planteó un compañero acerca de si existía una manera de, teniendo la siguiente función:

def isA[T](t: Any): Boolean = ???

comprobar si el valor t era de tipo T.
El primer paso obvio para comprobarlo sería algo como:

scala> def isA[T](t: Any): Boolean = t.isInstanceOf[T]
<console>:7: warning: abstract type T is unchecked since it is eliminated by erasure
       def isA[T](t: Any): Boolean = t.isInstanceOf[T]
                                                   ^
isA: [T](t: Any)Boolean

El compilador está avisando (y el que avisa no es traidor) que no tenemos información sobre T en tiempo de runtime, que se está perdiendo. ¿Qué ocurre si le pasamos distintos tipos? Se los zampa:

scala> isA[Int]("")
res2: Boolean = true

scala> isA[Int](3)
res3: Boolean = true

En este caso dijimos “bueno, ¿porque no tratamos de comprobar si las clases son asignables entre sí, obteniendo información del ClassTag“?
Pues ocurría que,

scala> val x: Any = 3
x: Any = 3

scala> x.getClass
res2: Class[_] = class java.lang.Integer

scala> classOf[Int].isAssignableFrom(classOf[Int])
res3: Boolean = true

scala> classOf[Int].isAssignableFrom(classOf[java.lang.Integer])
res4: Boolean = false

La clase de x es java.lang.Integer, que no es asignable por parte de Int (lo mismo ocurría con variedad de tipos primitivos).

Al final…

Finalmente nos dimos cuenta de que ClassTag tiene un maravilloso método unapply que permite extraer un valor tipado si este pertenece a la clase del ClassTag. Justo lo que necesitábamos:

def isA[T:ClassTag](t: Any): Boolean = 
  classTag[T].unapply(t).isDefined

al probarlo en la REPL comprobamos con alegría que:

scala> val x: Any = 3
x: Any = 3

scala> isA[String](x)
res5: Boolean = false

scala> isA[Int](x)
res6: Boolean = true

Conclusiones

No es habitual tener que lidiar con estos temas salvo que se estén construyendo sistemas lo suficientemente genéricos. No obstante, nunca está de más echar un vistazo al lado oscuro…

137134463824621

¡Agur de limón!

Curry, please…

One of the Scala’s rock starts that we cannot miss the chance to speak about is currying.

4252082-curry

What theory says

If we have a function (T,U) => V, currying it implies decomposing the function in a simpler one that allows building the result incrementally. In that case, we would get a function T => (U => V), what means that, from a T value we get a function whose only need is a U value for generating a V one. Messy? Let’s take a better look with next example.

Let’s suppose that we have a case class for modeling the student entity:

case class Student(
  name: String,
  age: Int,
  enrolled: Boolean)

We could have a method for instantiate a student: oh wait, like method apply, which has been automatically generated by creating the case class:

//Auto generated code below
object Student {

  def apply(
    name: String, 
    age: Int, 
    enrolled: Boolean): Student =
    new Student(name, age, enrolled)

}

By using such method, we can create a Student as shown below:

Student("john", 18, enrolled=true)

So easy so far. So let’s imagine next requirement:

In our student admission process, the candidate has to provide his/her personal data sequentially in a set of windows (At window A name must be provided. At window B so must the age. And finally, at window C, we would admit or not the candidate by setting the ‘enrolled’ attribute).

First approach: Classes are free!

We can define our windows as type aliases of transforming functions (so redundant…). I mean:

type WindowA = String => NotAStudientYet
type WindowB = (NotAStudentYet, Int) => AlmostAStudent
type WindowC = (AlmostAStudent, Boolean) => Student

case class NotAStudentYet(name: String)
case class AlmostAStudent(name: String, age: Int)

Take a look that windows are represented as functions.
So first window is a function that, given a name, it generates a “not-a-student-yet-like” object.
Second window takes as parameters a NotAStudientYet and the age of the subject, and it returns an “almost-a-student”.
And the last one takes an “almost-a-student” and an admission or rejection parameter, which will finally allow generating a Student.

So for our purpose, with this first approach, we have created a couple of new classes that will be used as data-accumulators for, at the end, creating a Student.

The implementation should look like:

val windowA: WindowA = 
  (name) => 
    NotAStudentYet(name)

val windowB: WindowB = 
  (notStudent, age) => 
    AlmostStudent(notStudent.name, age)

val windowC: WindowC = 
  (almost, enrolled) => 
    Student(almost.name, almost.age, enrolled)

…sincerely, there’s no way to think that for doing such a thing, we have define additional classes. Let’s try another way.

Second approach: functions, functions everywhere …

Let’s have a try to defining functions that return another functions (higher order functions):

type WindowA = String => WindowB
type WindowB = Int => WindowC
type WindowC = Boolean => Student

val windowA: WindowA = 
  (name: String) => {
    val windowB: WindowB =
      (age: Int) => {
        val windowC: WindowC =
          (enrolled: Boolean) =>
            Student(name, age, enrolled)
        windowC
      }
    windowB
  }

By using a bunch of little functions, we’re setting values to all parameters that will build our Student. It’s pretty easier if we try to read the code from the most inside function to the most outside one (first windowC, then windowB and finally windowA). For invoking our function it’s enough with executing:

val student = windowA("john")(18)(true)

Third approach: U sure there’s nothing existing for this?

Of course it is. Inside the Function companion object in Scala, you can find curried method, which purpose is to separate a function that takes N parameters, in N concatenated functions, as we were previously discussing and with the last example as well.

For applying this wonder to the exposed example, it’s as easy as writing:

val f = (Sudent.apply _).curried
//f: String => (Int => (Boolean => Student))

f("john")(18)(true)
//Student("john", 18, true)

The reverse function called uncurried can also be found at the same Function companion object, so that N concatenated functions, for example, Int => (String => (Boolean => Double))) are converted to a single function that takes N different parameters: (Int, String, Boolean) => Double:

val myApply = Function.uncurried(f)
//myApply: (String, Int, Boolean) => Student

myApply("john",18,true)
//Student("john",18,true)

Easy peasy.
Peace out! 🙂

Curry, por favor…

Uno de los aportes de Scala de los que no podemos dejar pasar la ocasión de hablar acerca de ellos es el currying.

4252082-curry

La teoría

Si tenemos una función (T,U) => V, currificar la función supone descomponer la función en otra más sencilla que permite construir el resultado de manera incremental. En este caso pasaríamos a tener una función T => (U => V), es decir, a partir de un T obtenemos una función que solo necesita un U para generar un V. ¿Lioso? Veámoslo mejor con el siguiente ejemplo.

Supongamos que tenemos una case class que modela un estudiante:

case class Student(
  name: String,
  age: Int,
  enrolled: Boolean)

Podríamos tener adicionalmente un método que nos instanciara un estudiante como, por ejemplo, el método apply que se ha generado automáticamente para la case class:

//Auto generated code below
object Student {

  def apply(
    name: String, 
    age: Int, 
    enrolled: Boolean): Student =
    new Student(name, age, enrolled)

}

Utilizando dicho método, podemos construir un estudiante como sigue:

Student("john", 18, enrolled=true)

Hasta aquí fácil. Ahora supongamos la siguiente situación:

En nuestro proceso de admisión de alumnos, el candidato tiene que pasar por una serie de ventanillas para aportar su documentación poco a poco (en la ventanilla A indicaría el nombre, en la ventanilla B indicaría la edad; y en la ventanilla C le daríamos el visto bueno, o no, para formar parte de la escuela).

Primera aproximación: Hacer clases es gratis

Podemos definir nuestras ventanillas como alias de funciones transformadoras. Es decir:

type WindowA = String => NotAStudientYet
type WindowB = (NotAStudentYet, Int) => AlmostAStudent
type WindowC = (AlmostAStudent, Boolean) => Student

case class NotAStudentYet(name: String)
case class AlmostAStudent(name: String, age: Int)

Fijaros que, por una parte, las ventanillas se representan mediante funciones.
La primera ventanilla es una función que, a partir de un nombre, genera algo “que aún no es estudiante”.
La segunda ventanilla, teniendo algo “que aún no es estudiante” y recibiendo una edad, devuelve algo que “casi es un estudiante”.
Y la última ventanilla recibe algo “que casi es un estudiante” y una aprobación de admisión (aprobada o denegada) y genera un estudiante.

Para ello, en esta primera aproximación, hemos generado dos case classes nuevas, que van a servir de acumuladores, para finalmente crear un estudiante.

La implementación sería algo del estilo:

val windowA: WindowA = 
  (name) => 
    NotAStudentYet(name)

val windowB: WindowB = 
  (notStudent, age) => 
    AlmostStudent(notStudent.name, age)

val windowC: WindowC = 
  (almost, enrolled) => 
    Student(almost.name, almost.age, enrolled)

…sinceramente, no es posible que para hacer tal cosa tengamos que definirnos dos clases adicionales. Optemos por dar otro enfoque.

Segunda aproximación: Funciones, funciones everywhere …

Probemos a definir funciones que devuelvan otras funciones (funciones de orden superior):

type WindowA = String => WindowB
type WindowB = Int => WindowC
type WindowC = Boolean => Student

val windowA: WindowA = 
  (name: String) => {
    val windowB: WindowB =
      (age: Int) => {
        val windowC: WindowC =
          (enrolled: Boolean) =>
            Student(name, age, enrolled)
        windowC
      }
    windowB
  }

Fijaros que a partir de pequeñas funciones, vamos dando valores a los parámetros que construirán nuestro estudiante. Es más fácil si intentamos leerlo desde la función más interior a la mas exterior(primero windowC, después windowB y finalmente windowA). Para invocar nuestra función basta con ejecutar:

val student = windowA("john")(18)(true)

Tercera aproximación: ¿Seguro que no existe nada que haga esto?

Por supuesto que lo hay. Dentro del companion de Function en Scala, se encuentra el método curried, cuyo cometido es descomponer una función que recibe N argumentos en N funciones concatenadas, como veíamos al principio del post, y en el último ejemplo.

Para aplicar esta maravilla al ejemplo expuesto bastaría con escribir:

val f = (Sudent.apply _).curried
//f: String => (Int => (Boolean => Student))

f("john")(18)(true)
//Student("john", 18, true)

Existe además la función inversa uncurried, que dadas N funciones encadenadas, por ejemplo, Int => (String => (Boolean => Double))) devuelve una única función que recibe N argumentos: (Int, String, Boolean) => Double:

val myApply = Function.uncurried(f)
//myApply: (String, Int, Boolean) => Student

myApply("john",18,true)
//Student("john",18,true)

Fácil, sencillo y para toda la familia.
Agur de limón 🙂

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! 🙂

Seeking anonymity. Anonymous classes

What are anonymous classes?

As their name already suggests, anonymous classes are unnamed classes, classes that don’t have a name. They are the classes that corrupt people like the most. They are not declared… BA DUM, TSS!

Now seriously, how can I create an instance from an anonymous class? Easy, simply by using the reserved word new and defining the body with braces.

val myPoint = new{ val x = 1; val y = 2 }

This way, we create an instance that has two integer values: x and y. However, as can be appreciated, we have given no name to this class.

tGWoYYo

Syntactic sugar

A few weeks ago, we talked about traits and how we could create an instance from a trait by means of an anonymous class.
Perhaps at that time, a WTF the size of Sebastopol crossed your mind. If we take a look at that post, the syntax for creating an instance from a trait was something like this:

  trait AnonymousHero {
    def superpower: String
  }

  val myHero = new AnonymousHero {
    def superpower = "I can compile Scala with my brain"
  }

We are creating an instance from a trait! They are no classes anywhere! Is this black magic? Well, no, it’s sintactic sugar.

Actually, what is really going on underneath is something like this:

  class AnonymousHeroClass extends AnonymousHero {
    def superpower: String = "I can compile Scala with my brain"
  }

  val myHero = new AnonymousHeroClass

As can be seen, when instantiating a trait, what is really happening is that a class that extends that trait is created. After that, an instance of that class is created.

This way, instances can be created from traits without any boilerplate code.

Bonus-track: Anonymous functions

Lambda expressions can be used in Scala, that is, anonymous functions. Further on, in other posts we’ll get to the importance of the use of anonymous functions in, for instance, methods that accept functions as parameter.
Anonymous functions, just like anonymous classes, are functions that don’t need to be declared.
This is an example of anonymous function:

  (x: Int) => x + 1

In this case, the anonymous function expects an integer and returns that same integer plus one unit.

We must not forget that, in Scala, functions are objects in reality. By taking this last consideration into account, we can relate anonymous functions (or lambda expressions) to anonymous classes.

So, when we use the anonymous function (x: Int) => x + 1, what is really happening is the creation of an instance of an anonymous class from the trait Function1:

new Function1[Int, Int] {
  def apply(x: Int): Int = x + 1
}

Everything fits!

tGWoYYo

¿Implícito, Lisa, o ilícito?

Uno de los mayores temores de un iniciado a Scala es el uso de implícitos: no gustan, se evitan, dan miedo, se envían a Satán.

satan

Y es una pena que disponiendo de tan poderosa herramienta, por una cuestión de pánico escénico, se le relegue a un segundo plano, o no se use. Los implícitos pueden resultar una solución elegante para determinadas circunstancias que citaremos posteriormente.

Para comprender mejor a la criatura, establezcamos un marco teórico.

Valores implícitos

Imaginemos que estamos definiendo la función ‘identidad’ sobre las operaciones de los números enteros (Dado un entero y una operación, devolver el mismo número entero). En base al tipo de operación, el valor por el cual hay que operar (suma, multiplicación,…) es distinto. Por ejemplo, para la adición empleamos el 0 (0 + n = n) y para la multiplicación usamos 1 (1 * n = n). Para evitar pasar este elemento neutro de manera explícita podríamos hacer uso de los implícitos como sigue.

Primero nos declaramos un número entero implícito que representará nuestro elemento neutro para la suma:

implicit val addNeutralElement: Int = 0

Y ahora declaramos la operación identidad en la suma:

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

Fijaros bien que los parámetros implícitos en el método se declaran en otro grupo de argumentos y precedidos de la palabra reservada ‘implicit‘.
Otra observación a tener en cuenta es que lo importante no es el nombre del argumento en el método, sino el tipo: el compilador buscará en el ámbito del método, un valor implícito de tipo entero

Este mismo principio también aplica a métodos:

implicit def generateAddIdentity():Int = 0

…y objetos…

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

Implicit ambiguity / Scopes

Supongamos ahora que queremos definir la función identidad para la multiplicación. En el mismo ámbito podríamos definir otro valor implícito para el elemento neutro en la multiplicación:

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

Si tratamos de ejecutar cualquiera de los dos métodos…Woops! El compilador chilla algo ininteligible:

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)
                         ^

Lo que viene a querer decir es que no sabe cual de los dos valores implícitos debe tomar, es decir, existe ambigüedad de implícitos. Para evitar esto, se definen ámbitos que suelen venir dados por objetos. Algo del siguiente estilo:

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

//in your snippet...

{
  import AddStuff._
  addIdentity(2)
}

Ya se que esto pide a gritos una type-class, pero no robemos el minuto de fama a los implícitos…

Implicit classes

También podemos declarar clases implícitas en Scala. La función principal de esta herramienta es la de extender funcionalidad de determinadas clases. Por ejemplo, supongamos que estamos usando un framework de un tercero, y una de sus clases tiene la siguiente pinta:

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

Como no podemos modificar el código fuente de este framework, pero queremos añadir nuevos métodos a la clase ‘ThirdParties’ podemos definir una clase implícita como sigue:

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

De esta manera, cuando escribamos un valo de tipo ‘ThirdParties’ seguido de un método que no pertenece a su clase, el compilador buscará clases y/o métodos implícitos que encajen en dicha signatura. Así podremos usar, tanto los métodos definidos originalmente por la clase ‘ThirdParties’, como los métodos complementarios que acabamos de definir en la clase implícita:

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

Casos de uso

La pregunta del millón entonces es, ¿en qué casos podemos aplicar esta poderosa navaja suiza?

* Establecer contextos de ejecución
* Aportar información adicional sobre un parameter type (type classes)
* Generar DSLs
* Ampliar funcionalidad de una clase (implicit classes)

Dado que poner ejemplos de todos ellos sería muy extenso, veremos algunos de estos casos prácticos en futuros posts.

giphy

¡Agur de limón! 🙂