• No se han encontrado resultados

Las clases monádicas

In document Int. Agradable a Haskell (página 54-56)

9 Sobre las Mónadas

9.1 Las clases monádicas

El Prelude contiene varias clases que definen mónadas del modo en que son usadas en Haskell. Estas clases están basadas en el concepto de mónada que aparece en la teoría de categorías; aunque la terminología en esta teórica proporciona los nombres para las clases monádicas y las correspondientes operaciones, no es necesario ahondar en las matemáticas abstractas para obtener una idea intuitiva de cómo usar las clases monádicas.

Una mónada se construye sobre un tipo polimórfico como IO. La mónada propiamente queda definida mediante declaraciones de instancia que asocian el tipo con algunas o todas las clases monádicas, Functor, Monad, y MonadPlus. Ninguna de las clases monádicas puede ser derivada. Además de IO, otros dos tipos en el Prelude son miembros de las clases monádicas: las listas ([]) y el tipo Maybe.

Matemáticamente, las mónadas está caracterizadas por un conjunto de leyes (o

propiedades) que deberían cumplir las operaciones monádicas. Este concepto de ley no es exclusivo de las mónadas: otras de las operaciones de Haskell está caracterizadas, al menos informalmente, por leyes. Por ejemplo, x /= y y not (x == y) deberían ser lo mismo para cualquier tipo de valor para el que tenga sentido dicha comparación. Sin embargo, no hay garantía de de que esto ocurra: tanto == como /= son métodos independientes en la clase Eq y no hay modo de asegurar que == y =/ están relacionados de este modo. Del mismo modo, las leyes monádicas que presentamos a continuación no son impuestas por Haskell, pero deberían cumplirse para cualquier instancia de una clase monádica. Las leyes de las mónadas proporcionan un mayor entendimiento de la estructura subyacente de las mónadas: esperamos que al examinar estas leyes proporcionemos al lector una primera impresión de cómo se usan las mónadas.

La clase Functor, ya discutida en la sección 5, define una única operación: fmap. La función fmap aplica una operación a los objetos pertenecientes a un contenedor (los tipos polimórficos pueden ser considerados como contenedores de valores de otro tipo), devolviendo un contenedor con la misma forma. Estas leyes caracterizan a fmap en la

fmap id = id

fmap (f . g) = fmap f . fmap g

Estas leyes aseguran que la forma del contenedor no es modificada por fmap y que el contenido de un contenedor no es reorganizado por la aplicación.

La clase Monad define dos operaciones básicas: >>= (bind) y return.

infixl 1 >>, >>= class Monad m where

(>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a

fail :: String -> m a m >> k = m >>= \_ -> k

Las operaciones >> y >>=, combinan dos valores monádicos mientras que la función

return sumerge un valor en la mónada (el contenedor). El tipo de >>=, Monad m => m a - > (a -> m b) -> m b, nos ayuda a comprender esta operación: ma >>= \v -> mb combina un valor monádico ma que contenga valores de tipo a y una función que opera sobre sobre

un valor v de tipo a devolviendo el valor monádico mb. El resultado es combinar ma y mb

en un valor monádico que contenga b. El operador >> es usado cuando la función no usa el valor producido por la primera operación monádica.

El significado de los operadores bind depende, por supuesto, de la mónada. Por ejemplo, en la mónada IO (mónada de E/S), x >>= y lleva a cabo dos acciones secuencialmente, pasando el resultado de la primera a la segunda. Para las otras

mónadas predefinidas, las listas y el tipo Maybe, estas operaciones monádicas pueden ser entendidas en términos de pasar cero o más valores desde una computación a la

próxima. Veremos ejemplos de esto a continuación.

La notación do proporciona una abreviatura sintáctica simple para encadenamientos de operaciones monádicas. La esencia de traducción de las expresiones do está reflejada en las siguientes dos reglas:

do e1 ; e2 = e1 >> e2 do p <- e1; e2 = e1 >>= \p -> e2

Cuando el patrón en la segunda ecuación do es refutable, un fallo en el encaje de patrones llama a la función fail. Esto puede elevar un error (como ocurre en la mónada

IO) o devolver un `cero' (como ocurre en la mónada lista). Así, la traducción completa es

do p <- e1; e2 = e1 >>= (\v -> case v of p -> e2; _ -> fail "s")

donde "s" es una cadena de caracteres identificando la localización de la sentencia do

para un posible uso en un mensaje de error. Por ejemplo, en la mónada IO, una acción como 'a' <- getChar llamará a fail si el caracter tecleado no es 'a'. Esto, a su vez, terminará

Las leyes que caracterizan a >>= y return son:

return a >>= k = k a

m >>= return = m

xs >>= return . f = fmap f xs m >>= (\x -> k x >>= h) = (m >>= k) >>= h

La clase MonadZero se usa para mónadas que definen un elemento cero y una operación

plus:

class (Monad m) => MonadZero m where mzero :: m a

mplus :: m a -> m a -> m a

El elemento cero debe verificar las siguientes propiedades:

m >>= \x -> mzero = mzero mzero >>= m = mzero

Para las listas, el valor cero es [], la lista vacía. La mónada IO no posee un cero por lo que no es un miembro de esta clase.

Las propiedades que caracterizan al operador mplus son las siguientes:

m `mplus` mzero = m mzero `mplus` m = m

En el caso de las listas, el operador mplus es la concatenación habitual.

In document Int. Agradable a Haskell (página 54-56)

Documento similar