Translate

sábado, 14 de noviembre de 2020

Maybe


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