Veamos la mónada Maybe, que facilita mucho el uso del tipo Maybe.
Ya conoces la definición del tipo Maybe:
data Maybe a = Just a | Nothing
Las funciones de Head y Tail de Prelude no son seguras en el sentido de que fallan cuando se llaman en una lista vacía. Podemos definir versiones seguras usando Maybe:
myHead :: [a] -> Maybe a
myHead [] = Nothing
myHead (x:xs) = Just x
myTail :: [a] -> Maybe []
myTail [] = Nothing
myTail (x:xs) = Just xs
Ahora podemos hacer de Maybe una instancia de la clase de tipo Monad, simplemente proporcionando las definiciones apropiadas para return, bind, then y fail:
import Control.Monad
instance Monad Maybe where
return = Just
Nothing >>= f = Nothing
(Just x) >>= f = f x
fail _ = Nothing
Hay algunas funciones adicionales definidas en la clase de tipo MonadPlus:
instance MonadPlus Maybe where
mzero = Nothing
Nothing `mplus` x = x
x `mplus` _ = x
Eso es todo, ¡ahora tenemos una mónada Maybe!
Veamos qué nos da esta mónada:
Un cálculo usando Maybe explícito
foo :: [a] -> Maybe a
foo xs =
case myTail xs of
Nothing -> Nothing
Just a -> case myTail a of
Nothing -> Nothing
Just b -> myHead b
Para combinar cálculos que usan el tipo Maybe, necesitamos expresiones de caso explícitas para hacer coincidir el patrón con el tipo.
Escribamos este cálculo usando la mónada Maybe, primero usando el operador >> =:
bar :: [a] -> Maybe a
bar xs =
myTail xs >>=
(\a -> myTail a >>=
(\b -> myHead b))
Ahora cambiemos un poco los saltos de línea y la sangría para que se vea mejor:
bar2 :: [a] -> Maybe a
bar2 xs =
myTail xs >>= (\a ->
myTail a >>= (\b ->
myHead b))
Gracias a la ley de asociatividad, también podemos eliminar los paréntesis innecesarios:
bar3 :: [a] -> Maybe a
bar3 xs =
myTail xs >>= \a ->
myTail a >>= \b ->
myHead b
Esto ya es mucho más limpio, pero finalmente podemos usar la notación do:
bar3 :: [a] -> Maybe a
bar3 xs = do
a <- myTail xs
b <- myTail a
myHead b
Claramente, el código monádico final es mucho más legible que el código no monádico original.
Ejemplo: Reducción de barra [5,6]
bar [5,6]
-- > substitute [5,6] for xs in definition of bar
myTail [5,6] >>=
(\a -> myTail a >>=
(\b -> myHead b))
-- > def. myTail
Just [6] >>=
(\a -> myTail a >>=
(\b -> myHead b))
-- > def.2 of (>>=)
(\a -> myTail a >>=
(\b -> myHead b))
[6]
-- > beta reduction, substitute [6] for a
myTail [6] >>= (\b -> myHead b)
-- > reduce myTail
Just [] >>= (\b -> myHead b)
-- > def.2 of (>>=)
(\b -> myHead b) []
-- > beta reduction, substitute [] for b
myHead []
-- > def.1 myHead
Nothing
Ejemplo: Reducción de barra [5,6,7]
bar [5,6,7]
-> sustituir [5,6,7] por xs en la definición de bar
bar [5,6,7]
-- > substitute [5,6,7] for xs in definition of bar
myTail [5,6,7] >>=
(\a -> myTail a >>=
(\b -> myHead b))
-- > def. myTail
Just [6,7] >>=
(\a -> myTail a >>=
(\b -> myHead b))
-- > def.2 of (>>=)
(\a -> myTail a >>=
(\b -> myHead b))
[6,7]
-- > beta reduction, substitute [6,7] for a
myTail [6,7] >>= (\b -> myHead b)
-- > reduce myTail
Just [7] >>= (\b -> myHead b)
-- > def.2 of (>>=)
(\b -> myHead b) [7]
-- > beta reduction, substitute [7] for b
myHead [7]
-- > def myHead
Just 7