Do-notación desugars a (>> =) sintaxis de esta manera:
getPerson = do
name <- getLine -- step 1
age <- getInt -- step 2
return $ Just Person <*> Just name <*> age
getPerson2 =
getLine >>=
(\name -> getInt >>=
(\age -> return $ Just Person <*> Just name <*> age))
cada línea en do-notación, después de la primera, se traduce en un lambda que luego se une a la línea anterior . Es un proceso completamente mecánico para enlazar valores a nombres. No veo cómo el uso de notación do o no afectaría la compibilidad en absoluto; es estrictamente una cuestión de sintaxis.
Su otra función es similar:
getInt :: IO (Maybe Int)
getInt = do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt2 :: IO (Maybe Int)
getInt2 =
(fmap reads getLine :: IO [(Int,String)]) >>=
\n -> case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
algunas pistas de la dirección que parecen estar encabezado:
Al utilizar Control.Applicative
, a menudo es útil usar <$>
para levantar funciones puras en la mónada . Hay una buena oportunidad para ello en la última línea:
Just Person <*> Just name <*> age
convierte
Person <$> Just name <*> age
También, usted debe buscar en transformadores mónada. El paquete mtl está más extendido porque viene con la plataforma Haskell, pero hay otras opciones. Los transformadores de mónada le permiten crear una nueva mónada con el comportamiento combinado de las mónadas subyacentes. En este caso, está usando funciones con el tipo IO (Maybe a)
. El MTL (en realidad una biblioteca de la base, transformadores) define
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Este es el mismo que el tipo que está usando, con la variable instanciada en m
IO
.Esto significa que puede escribir:
getPerson3 :: MaybeT IO Person
getPerson3 = Person <$> lift getLine <*> getInt3
getInt3 :: MaybeT IO Int
getInt3 = MaybeT $ do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt3
es exactamente el mismo, excepto para el MaybeT
constructor. Básicamente, cada vez que tenga un m (Maybe a)
puede envolverlo en MaybeT
para crear un MaybeT m a
. Esto gana una capacidad de compilación más simple, como puede ver en la nueva definición de getPerson3
. Esa función no se preocupa por el fracaso en absoluto, ya que todo es manejado por la tubería MaybeT. La pieza restante es getLine
, que es solo IO String
. Esto se levanta en la mónada MaybeT con la función lift
.
Editar El comentario de newacct sugiere que también debería proporcionar un ejemplo de coincidencia de patrones; es básicamente lo mismo con una excepción importante. Considere este ejemplo (la lista mónada es la mónada que nos interesa, Maybe
es sólo allí por coincidencia de patrones):
f :: Num b => [Maybe b] -> [b]
f x = do
Just n <- x
[n+1]
-- first attempt at desugaring f
g :: Num b => [Maybe b] -> [b]
g x = x >>= \(Just n) -> [n+1]
Aquí g
hace exactamente lo mismo que f
, pero lo que si el ajuste de patrones falla?
Prelude> f [Nothing]
[]
Prelude> g [Nothing]
*** Exception: <interactive>:1:17-34: Non-exhaustive patterns in lambda
¿Qué está pasando? Este caso particular es el motivo de una de las verrugas más grandes (OMI) en Haskell, el método Monad
de la clase fail
. En la notación do, cuando se produce un error en la coincidencia de un patrón, se llama al fail
. Una traducción real estaría más cerca de:
g' :: Num b => [Maybe b] -> [b]
g' x = x >>= \x' -> case x' of
Just n -> [n+1]
_ -> fail "pattern match exception"
ahora tenemos
Prelude> g' [Nothing]
[]
fail
s utilidad depende de la mónada. Para las listas, es increíblemente útil, básicamente haciendo que la coincidencia de patrones funcione en las listas de comprensión. También es muy bueno en la mónada Maybe
, ya que un error de coincidencia de patrón daría lugar a un cálculo fallido, que es exactamente cuando Maybe
debe ser Nothing
. Para IO
, quizás no tanto, ya que simplemente arroja una excepción de error de usuario a través de error
.
Esa es la historia completa.
Véase también: [Haskell 2010 Informe> Expresiones # Hacer Expresiones] (http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-470003.14) –
Solo para destacar, tenga en cuenta que 'getPerson' isn No es una función, ya que no tiene '->' en su firma de tipo; si quieres un nombre más preciso que "valor", iría con "IO action". Ver ["¿Todo es una función?" En Haskell?] (Http://conal.net/blog/posts/everything-is-a-function-in-haskell) para más información sobre esto. –