Scalaz IX: Mónada Reader

En la presente entrada, Scalaz IX: Mónada Reader, realizaré una descripción de ciertos tipos de funciones y abstracciones hasta llegar a definir una Monada de lectura o Mónada Reader, mónada que permite un parámetro de entrada.

En lenguaje Scala, una función se puede definir como una variable; esta función, es traducida como una clase. En el siguiente ejemplo, se definen las funciones f1 y f2 como variables y se muestra un ejemplo de uso. Con Scalaz, estas funciones se pueden interpretar como functores, con lo cual, podemos concatenar llamadas a dichas funciones con la función map. Esta situación, nos permite ejecutar una secuencia de funciones con un parámetro de entrada.

import scalaz.Scalaz._
val f1 = (_: Int) * 2
val f2 = (_: Int) + 10
println(s"f1(2)=${f1(2)}")
println
println(s"f2(2)=${f2(2)}")
println
println(s" (f1 map f2)(2)=${(f1 map f2) (2)} ") // Función map como functor
println

La salida por consola es la siguiente:

f1(2)=4
f2(2)=12
(f1 map f2)(2)=14

Como observamos en el ejemplo anterior, podemos definir una parametro de lectura identificando el tipo de entrada, en concreto, un valor de tipo entero.

Las funciones de tipo Applicative, nos permite ejecutar varias funciones y, una vez ejecutadas, realizar un cálculo con los resultados de las aplicaciones. En el siguiente ejemplo, muestro un ejemplo de una función applicative en donde se ejecutan dos funciones (f1 y f2) y sus resultado son sumados.

import scalaz.Scalaz._
val f1Applicative = ({
  (_: Int) * 2
} |@| {
  (_: Int) + 10
}) (_ + _)
println(s"f1Applicative(2)=${f1Applicative(2)}")
println

La salida por consola es la siguiente:

f1Applicative(2)=16

Como observamos en el ejemplo anterior, podemos definir una parametro de lectura identificando el tipo de entrada, en concreto, un valor de tipo entero.

Para finalizar, podemos definir una mónada, representado con un for comprehension, que define dos cálculos: el primero, la multiplicación de un tipo entero por dos; el segundo, la suma de un valor entero mas 10; y, para finalizar, la suma de su resultado. El código de ejemplo es el siguiente:

import scalaz.Scalaz._
val f1Monada: Int => Int = for {
  a <- (_: Int) * 2
  b <- (_: Int) + 10
} yield {
  a + b
}
println(s"f1Monada=${f1Monada(2)}")
println

La salida por consola es la siguiente:

f1Monada=16

Como observamos en el ejemplo anterior, podemos definir una mónada con un valor de entrada especificando su tipo. Así, podemos interpretar que una mónada es de lectura porque podemos definir valores de entrada.

Para el lector interesado, las entradas que he realizado sobre Scalaz hasta la fecha son las siguientes:

Scalaz VIII: Construcción de funciones con parámetros con Applicative

En la tercera entrada de la serie, Scalaz III: Apply y Applicative , me centre en la descripción de las API Apply y Applicative. El API Apply nos permite aplicar funciones a functores; y, el API Applicative, permite mapear funciones con N parámetros. En la presente entrada, Scalaz VIII: Construcción de funciones con parámetros con Applicative, describiré la forma de construir funciones de funciones con parámetros.

La definición de una función que realiza la suma de dos enteros de tipo Option con Applicative se puede realizar de la siguiente forma:

println(s"(3.some |@| 5.some) {_ + _}=${(3.some |@| 5.some) {_ + _}}" )

La salida por consola es la siguiente:

(3.some |@| 5.some) {_ + _}=Some(8)

La suma de estos dos elementos es sencilla pero, si necesito aplicar una serie de funciones para un valor de entrada, necesito definir una estructura de función parecida; para ello, definiremos tantas funciones como necesitemos con la definición de cada parámetro. En los siguientes apartados, muestro unos ejemplos de funciones con dos y con tres funciones.

Definición de una función de dos funciones

La definición de una función que realiza la suma de otras dos funciones las cuales, la primera, realiza la multiplicación de un entero por 2; y, la segunda, realiza la suma de un entero mas 10, se define de la siguiente forma:

val applicativeBuilder1 = ({(_:Int) * 2} |@| {(_:Int) + 10}) {_ + _}
println(s"( {(_:Int) * 2} |@| {(_:Int) + 10}) {_ + _}=${applicativeBuilder1(2)}")
println

La salida por consola es la siguiente:

({(_:Int) * 2} |@| {(_:Int) + 10}) {_ + _}=16

Definición de una función de tres funciones

La definición de una función que realiza la suma de tres funciones las cuáles, la primera, realiza la multiplicación de un entero por 2; la segunda, realiza la suma de un entero más 10; y, la tercera, realiza la suma de un entero mas 20, se define de la siguiente forma:

val applicativeBuilder2 = ({(_:Int) * 2} |@| {(_:Int) + 10} |@| {(_:Int) + 20}) {_ + _ + _}
println(s"({(_:Int) * 2} |@| {(_:Int) + 1}) {_ + _}=${applicativeBuilder2(2)}")
println

La definición de una función que realiza la multiplicación de dos enteros y al resultado se le suma una función las cuáles, la primera, realiza la multiplicación de un entero por 2; la segunda, realiza la suma de un entero más 10; y, la tercera, realiza la suma de un entero mas 20, se define de la siguiente forma:

val applicativeBuilder3 = ({(_:Int) * 2} |@| {(_:Int) + 10} |@| {(_:Int) + 20}) {_ * _ + _}
println(s"({(_:Int) * 2} |@| {(_:Int) + 1}) {_ * _ + _}=${applicativeBuilder3(2)}")
println

La salida por consola de las funciones son las siguientes:

({(_:Int) * 2} |@| {(_:Int) + 1}) {_ + _ + _}=38
({(_:Int) * 2} |@| {(_:Int) + 1}) {_ * _ + _}=70

En el caso que necesitemos tener funciones con distintos parámetros, utilizaremos el API Applicative como el descrito en la tercera entrada de la serie.

Para el lector interesado, las entradas que he realizado sobre Scalaz hasta la fecha son las siguientes:

Scala VII: leyes matemáticas de las mónadas

En la presente entrada, Scala VII: leyes matemáticas de las mónadas, me centraré en identificar las leyes de las mónadas y unos ejemplos que demuestran dichas leyes.

Las leyes matemáticas por las que se rigen las mónadas son dos: propiedad de elemento neutro o elemento de identidad y propiedad asociativa.

La demostración de la propiedad de la propiedad del elemento neutro por la izquierda y por la derecha queda definida en el siguiente snippet de código:

import scalaz.Monad
import scalaz.Scalaz._
def identidadPorLaIzquierda(): Unit = {
  Monad[Option].point("izquierda").>>=({ x => (x + " OK").some }).assert_===("izquierda OK".some)
}
def identidadPorLaDerecha(): Unit = {
  (("OK").some).>>=( x => Monad[Option].point(x + " derecha")).assert_===("OK derecha".some)
}

La propiedad  asociativa para la mónada queda definda en el siguiente snippet:

def asociatividad(): Unit = {
  Monad[Option].point(4).>>=({ x => (x + 4).some }).>>=({ y => ( y + 2 ).some }).assert_===(
  Monad[Option].point(4).>>=({ x => (x + 2).some }).>>=({ y => ( y + 4 ).some }) )
}

Como se observa en el código anterior, el resultado de cálculo de las funciones monádicas es el mismo ; con lo cual, el orden de ejecución de las funciones de suma de 4 y de 2 es el mismo.

Para el lector interesado, las entradas que he realizado sobre Scalaz hasta la fecha son las siguientes: