Scalaz X: Mónada estado

En la entrada anterior, Scalaz IX: Mónada Reader, realicé una descripción de una mónada de lectura; en esta nueva entrada, Scalaz X: Mónada estado, realizaré la descripción de la mónada estado. Para una mejor compresión de la mónada estado, es necesario entender lo que es la mónada de lectura.

Un mónada de estado es aquella mónada que dado un estado inicial, retorna una tupla con una estado siguiente y un valor. La definición matemática la podemos definir de la siguiente manera:

S' -> (S'', R)

Siendo: S’, el estado inicial; S”, el estado siguiente; y, R, el valor.

En Scalaz, la sintáxis de la mónada estado se define en el paquete scalaz.State. La mónada estado es pensada para aquellos problemas que requieran de una computación por estados.

Para realizar la explicación de la mónada estado, realicemos un ejemplo práctico. Definieremos la computación para determinar si se ha leído la palabra “01” y, para ello, necesitamos definir lo siguiente:

  1. Definir una variable estado, así como, sus posibles estados.
  2. Definir las transiciones de estado; transiciones, definidas con mónada estados.
  3. Flujo de lectura

La definición de un estado puede ser definida en un objeto de un determinado tipo, así como, los posibles estados por los que transite. Para el ejemplo, defino el tipo estado como un objeto entero y sus posibles estados. Además, defino el tipo del valor a retornar por la mónada estado.

type Estado = Int
val ESTADO_0:Int = 0 // Estado inicial
val ESTADO_1:Int = 1 // Se ha leído la letra 0
val ESTADO_2:Int = 2 // Estado final, se ha leído la palabra 01.
type Resultado = Boolean

Las transiciones entre estados están definidas con las mónadas estados, las cuales definen la funcionalidad a realizar. En nuestro problema, tenemos que definir dos transiciones: la primera, para determinar si se ha leído el 0; y, la segunda, para determinar si se ha leído el 1. Evidentemente para cada transición, es necesario pasar el valor del evento que produce la transición. En nuestro caso, las mónadas estados con las transiciones son las siguientes:

def leerCero (elemento:Int):State[Estado, Resultado] = State[Estado, Resultado] {
  case estado if ESTADO_0.equals(estado) && elemento.equals(0) => (ESTADO_1, false)
  case _ => (ESTADO_0, false)
}
def leerUno(elemento:Int):State[Estado, Resultado] = State[Estado, Resultado] {
  case estado if ESTADO_1.equals(estado) && elemento.equals(1) => (ESTADO_2, true)
  case _ => (ESTADO_1, false)
}

Un aspecto importante, es entender que el estado es inherente a la mónada estado y se asigna en la creación de la mónada;y, el valor del evento, es el valor pasado por parámetro. Así, una posible iteración es la definida en la siguiente función:

def ejemploSencillo:Unit = {
  val estado:Estado = ESTADO_0
  val (estado1, resultado1) = leerCero(0)(estado)
  println(s"estado1=${estado1} resultado1=${resultado1}")
  val (estado2, resultado2) = leerUno(1)(estado1)
  println(s"estado2=${estado2} resultado1=${resultado2}")
}

La salida por consola es la siguiente:

estado1=1 resultado1=false
estado2=2 resultado1=true

Del ejemplo anterior, se realiza la definición del estado inicial y se realizan las llamadas a las transiciones con los respectivos valores correspondientes a cada evento.

El ejemplo anterior es sencillo pero no es un caso muy real; para transformarlo, voy a definir la misma lógica determinando cuándo se ha leído desde consola la palabra “10”. Para ello, defino un bucle y el conjunto de transiciones. El ejemplo queda definido en el siguiente snippet:

def ejemploIterativo: Unit = {
  var estado: Estado = ESTADO_0
  var encontrada: Boolean = false
  while(!encontrada){
    print("Introduce un numero->")
    (estado) match {
    case ESTADO_0 => {
      val evento=scala.io.StdIn.readInt()
      val (estado0, resultado0) =leerCero(evento)(estado)
      estado = estado0
    }
    case ESTADO_1 => {
      val evento=scala.io.StdIn.readInt()
      val (estado0, resultado0) = leerUno(evento)(estado)
      estado = estado0
      if(resultado0){
        // encontrada = true
        println("PALABRA ENCONTRADA")
        estado = ESTADO_0
      }
   }
 }
}

La salida por consola es la siguiente:
Introduce un numero->0
Introduce un numero->1
PALABRA ENCONTRADA
Introduce un numero->
[...]

Como se observa en el ejemplo anterior, para el curioso lector, se ha dejado comentado el flag de asignación de fin de iteración.

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