Valores y funciones implícitas en Scala

Scala permite definir valores, funciones y objetos implícitos. Esto es: que se pueden usar sin necesidad de ser llamados directamente (explícitamente).  Aquí nos ocuparemos de los primeros dos, y hablaré de los objetos implícitos en otro tema donde son ampliamente usados.

Para ello, usamos la palabra reservada implicit antes de la declaración. Aquí un ejemplo:

implicit val listOfInts = (1 to 10) toList

Esto define el valor (inmutable) listOfInts, que no es nada más que List(1,2,3,4,5,6,7,8,9,10), pero el hecho de declararlo implícito nos permite que sea usado en cualquier lugar (dentro del alcance del programa en cuestión) en donde una lista de enteros se necesite. Para ello, necesitamos decirle a Scala que la lista de enteros puede ser implícita al momento de declarar el parámetro. Por ejemplo, digamos que queremos una función que nos permita calcular la suma de los enteros incluidos en una lista:

// Noten la declaración del parámetro como "implicit"
def sumElements(implicit l: List[Int]) = l.reduce{_ + _}

Esto nos permite llamar a la función de 2 formas: con o sin parámetro explícito. Como la función declara su parámetro como implícito, en caso de que no se provea Scala buscará un valor implícito del tipo requerido (en este caso una lista de enteros) que haya sido declarado en algún lugar dentro del alcance del programa.

def sumElements(implicit l: List[Int]) = l.reduce {_ + _}

def anotherFunction = {
  implicit val listOfInts = (1 to 10) toList

   // Código que intenta dominar al mundo

  /* Llamando a la función sin parámetros.
     Esto usará listOfInts, ya que está declarada
     como implícita.
   */
  sumElements 


  /*
    Aquí la función es llamada con parámetro explícito.
  */
  sumElements(List(2,4,6,8,10))
}

En caso de que una función tenga más de un parámetro, Scala permite que el último sea declarado implícito siempre y cuando esté en una lista de parámetros diferente. Hay que recordar que Scala permite tener varias listas de parámetros en las funciones, algo que fue mencionado cuando se explicó currying y aplicación parcial de funciones.

/* Esta función recibe 2 parámetros, pero el segundo es declarado implícito
   En vez de ser:
   def printMessage(msg: String, suffix: String)
  
   usamos 2 listas de parámetros
*/

def printMessage(msg: String)(implicit suffix: String) = 
    println("%s %s".format(msg, suffix))


// Ahora podemos llamar a la función con el segundo parámetro implícito.

implicit val naruto = "dattebayo!"

// Imprime "Algo que diga naruto dattebayo!"
printMessage("Algo que diga naruto")

/* Aquí pasamos explícitamente el segundo parámetro.
   Usamos a Lum-chan, de Urusei Yatsura
*/
printMessage("Daisuki da")("ccha!")

Hay que tener cuidado en no declarar dos valores implícitos del mismo tipo, ya que Scala no sabrá cuál debe usar y regresará un error indicándonos eso.

// Esto no compila
implicit val ja = "jajaja"

// Código para intentar dominar al mundo...

implicit val je = "jejeje"

Aquí estamos declarando 2 valores implícitos de tipo String, lo cual no es válido.

También es posible acceder a lo que está en contexto implícito usando implicitly[T]. Por ejemplo:

implicit val l = List("Hola", "a", "todos")

// En algún lugar más adelante

/*
  Como en contexto sólo puede haber un implícito de 
  cada valor, implicitly lo buscará.
  Esto imprime:
  Hola
  a
  todos
*/
implicitly[List[String]] foreach println

Un uso interesante de implícitos es con funciones que permiten transformar de un tipo a otro de forma transparente. Por ejemplo, digamos que queremos agregar una función “inc” a la clase Int. Lo primero que viene a la mente es crear una clase que extienda a Int, pero si Int está declarada como <em>final</em> no hay nada que se pueda hacer.

/* inc originalmente no es método de Int,
   por lo que este código no compilará
   por sí solo
*/

def addOne(x: Int) = x.inc

Para este tipo de casos, Scala nos permite definir una clase que tome como attributo un valor del tipo requerido y agregue la función que queremos definir.

/* Primero declaramos la clase con el tipo
   requerido y agregamos la función que
   deseamos que tenga */

class IncrementableInteger(x: Int) {
  def inc = x + 1
}

Ahora, en vez de tener que crear explícitamente una instancia de esa clase para usar el método inc, lo que hacemos es hacer esa transformación de forma implícita:

/* Con esta declaración, podemos usar inc en
   cualquier Int, sin necesidad de hacer nada
   más
*/

implicit def toIncrementableInteger(x: Int) = new IncrementableInteger(x)

// Esto ya funciona
println(1.inc)

Y con esto, ahora sí podemos usar la función addOne, ya que cuando una clase no tiene un método, Scala buscará si otra clase lo tiene y si hay una conversión implícita de un tipo a esa clase. De ser así, Scala aplica esa conversión y usa el método de la clase que lo contiene. Esta técnica se conoce como “Pimping Types“, algo así como “adornar tipos” o “mejorar tipos”, y es un concepto que también existe en C# bajo el nombre de “extensions”.

Los implícitos parecen ser más “syntactic sugar” que otra cosa, y así como tienen defensores también tienen detractores. Sin embargo, su uso hace que la llamada a muchas funciones sea más clara, con el precio de que la declaración de la misma puede ser un poco más compleja.

Como punto adicional, los implícitos en Scala son necesarios al momento de usar conceptos más avanzados, como typeclasses, de las que hablaré en la siguiente entrada de esta categoría.

10 Replies to “Valores y funciones implícitas en Scala”

  1. Muyy buena entrada !!! la mejor explicación de implícitos y explícitos que he encontrado, muchas gracias Manuel.

    Pregunta, en Japon ya todo es scala o hay mercado todavía para java ? aca en Colombia últimamente se esta viviendo una revolución con las tecnologías de typesafe (https://typesafe.com/) y aplicaciones reactivas, como esta el mercado en Japon ?

    Un saludo y los mejores deseos.

    1. ¡Muchas gracias por tu comentario!

      ¿Acá? Nah. Scala es usado sólo en algunas compañías. Está ganando adeptos, sí, pero el porcentaje es mínimo en comparación con Java, el cual todavía tiene gran mercado por este lado del charco.

      En donde laboro, nada más yo uso Scala, y es sólo para proyectos internos, ya que aquí o le tienen miedo al lenguaje o lo aborrecen por no ser dinámico. Uno de mis colegas es “rubyist”, y tiene una hostilidad bárbara hacia Scala simplemente porque no entiende el código, especialmente cuando uso applicatives o Scalaz (que ni bueno soy todavía, pero le hago la lucha).

      ¡Saludos!

      1. genial Manuel !!! gracias por la información… llegas con scalaz y ayer precisamente me recomendaron que le echara un ojo !!! tocara ver que tal, un saludo bro.

  2. Saludos!… Cuanto cobras las clases allá? jajaja. Me gusta programar pero aún estoy verde.

  3. saludos manuel, antes que nada buen post, tengo curiosidad por aprender de diferentes lenguajes pero para ser honesto no tengo mucho conocimiento de scala, la verdad se poco al igual que haskell. Por lo que veo tu estas trabajando mucho con estos dos, me gustaria que en alguna ocasión escribieras sobre C++ , C, o java ya que tengo un poco mas de experiencia y me gustaria leer algún post sobre programación de alguno de estos lenguages por parte tuya.

    1. Saludos 🙂

      Sobre los otros lenguajes:
      – C, C++. Tengo AÑOS que no los toco. Hace un par de años trabajé (a fuerzas) con C# y me llevé una gran sorpresa al encontrarme con un lenguaje así de maduro. Eso y F# es lo único que me motivaría a aprender .NET. Por lo demás, ni C ni C++ los he usado desde que llegué a Japón, por lo que puedo considerarme un completo NOOB en ellos.

      – Java. Hay varios posts en los que uso o explico algo en Java, y en mi Github en este momento tengo 2 programas en ese lenguaje. Últimamente casi no programo en él, pero superviso y reviso código, por lo que no ando tan perdido, pero en lo personal no me gusta, ni me gustaría, regresar a Java después de haber trabajado con Scala y, en menor medida, con Groovy, y haber leído lo que Clojure puede hacer.

      De momento me estoy enfocando a programación funcional usando Scala. Haskell le muevo mucho menos porque casi no he hecho proyectos en él.

Leave a Reply to Manuel Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.