For comprehension y la importancia de la belleza

Existen multitud de dudas existenciales en el mundo moderno en el que vivimos. Pero hay una duda que supera a todas ellas. ¿Qué hace realmente una for comprehension?

La respuesta a esta intrincada cuestión es….nada nuevo. Las estructuras for comprehension son simplemente syntactic sugar. Por debajo, en función de como construyamos el bloque, habrá llamadas a map, flatmap, withFilter o foreach.

Las for comprehension contarán con dos partes clave:

  • El bloque for mediante el cual se realizan consultas anidadas
  • El bloque yield donde se agrupan y tratan las consultas realizadas anteriormente

Para entender estos conceptos vamos a ver un pequeño ejemplo:

for {
  x <- List(1, 2, 3)
  y <- List(true, false) 
} yield (x, y)

El resultado de esta expresión será la combinación mediante tuplas de la primera lista con la segunda:

List(
  (1, true),
  (1, false),
  (2, true),
  (2, false),
  (3, true),
  (3, false)
)

Esta for comprehension realmente realizará las siguientes acciones:

List(1, 2, 3).flatMap(x =>
  List(true, false).map( y => (x, y))
)

Como se puede observar, mediante los bloques for todo se vuelve mucho más legible. Lo único que hacemos es hilar varios flatmap para culminar con un map. Vamos a ver otro ejemplo:

for {
  x <- List(1, 2)
  y <- List(true, false)
  z <- List("a", "b") 
} yield (x, y, z)

cuyo equivalente será:

List(1, 2).flatMap(x =>
  List(true, false).flatMap( y =>
    List("a", "b").map(z => (x, y, z))
  )
)

y el resultado producido será:

List(
  (1, true, "a"),
  (1, true, "b"),
  (1, false, "a"),
  (1, false, "b"),
  (2, true, "a"),
  (2, true, "b"),
  (2, false, "a"),
  (2, false, "b")
)

Además, también podemos utilizar filtros mediante sentencias if embebidas:

for {
  x <- List(1, 2, 3, 4, 5, 6, 7)
  if x < 3
  y <- List("a", "b") 
} yield (x, y)

//result: List((1,a), (1,b), (2,a), (2,b))

cuyo equivalente es:

List(1, 2, 3, 4, 5, 6, 7).withFilter(_ < 3).flatMap( x => 
  List("a", "b").map(
    y => (x, y)
  )
)

Existe la posibilidad de que no solo queramos crear una nueva colección, sino realizar una acción con cada uno de los elementos. En ese caso no utilizaremos el bloque yield. Esto provocará que la traducción, al no querer realizar transformaciones a una colección, en vez de ser un método map, sea mediante el método foreach. Tiene bastante sentido ya que en este caso solo queremos realizar acciones por cada evento generado y el resultado final será de tipo Unit.

for {
  x <- List(1, 2, 3)
  y <- List(true, false) } println(s"($x , $y)") List(1, 2, 3).foreach(x =>
  List(true, false).foreach( y => 
    println(s"($x , $y)")
  )
)

Después de todos estos ejemplos, esperamos que utilicéis las for comprehension para hacer vuestro código más legible siempre que sea posible. Aunque todos sepamos que la magia se crea en el interior.

35dunb

3 comentarios en “For comprehension y la importancia de la belleza

  1. List(1, 2, 3).flatMap(x =>
    List(true, false).flatMap( y =>
    List(«a», «b»).map(z => (x, y, z))
    )
    )

    no debería ser

    List(1, 2).flatMap(x =>
    List(true, false).flatMap( y =>
    List(«a», «b»).map(z => (x, y, z))
    )
    )

    Me gusta

Deja un comentario