Do-notazione desugars a (>> =) sintassi in questo modo:
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))
ogni linea di fai notazione, dopo il primo, si traduce in un lambda che viene quindi associato alla riga precedente . È un processo completamente meccanico per legare valori ai nomi. Non vedo come l'uso della notazione o non influenzi affatto la componibilità; è strettamente una questione di sintassi.
tua altra funzione è simile:
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
Alcune indicazioni per la direzione ti sembra di essere a capo:
Quando si utilizza Control.Applicative
, è spesso utile utilizzare <$>
per sollevare funzioni pure nella monade . C'è una buona opportunità per questa nell'ultima riga:
Just Person <*> Just name <*> age
diventa
Person <$> Just name <*> age
Inoltre, si dovrebbe guardare in trasformatori monade. Il pacchetto mtl è molto diffuso perché include la piattaforma Haskell, ma ci sono altre opzioni. I trasformatori di Monad ti permettono di creare una nuova monade con il comportamento combinato delle monadi sottostanti. In questo caso, stai utilizzando le funzioni con il tipo IO (Maybe a)
. Il mtl (in realtà una libreria base, trasformatori) definisce
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Questo è lo stesso del tipo in uso, con la variabile m
un'istanza a IO
.Ciò significa che è possibile scrivere:
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
è esattamente lo stesso, tranne per il costruttore MaybeT
. In pratica, ogni volta che si dispone di un m (Maybe a)
è possibile eseguire il wrapping in MaybeT
per creare un MaybeT m a
. Ciò consente di ottenere una composizione più semplice, come si può vedere dalla nuova definizione di getPerson3
. Quella funzione non si preoccupa affatto del fallimento perché è tutto gestito dall'impianto idraulico di MaybeT. L'altro pezzo è getLine
, che è solo un IO String
. Questo viene sollevato nella monade MaybeT dalla funzione lift
.
Modifica Il commento di newacct suggerisce che dovrei fornire anche un esempio di corrispondenza del modello; è praticamente lo stesso con un'eccezione importante. Si consideri questo esempio (l'elenco monade è la monade che ci interessa, Maybe
è lì solo per pattern matching):
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]
Qui g
fa esattamente la stessa cosa di f
, ma cosa succede se il pattern match fallisce?
Prelude> f [Nothing]
[]
Prelude> g [Nothing]
*** Exception: <interactive>:1:17-34: Non-exhaustive patterns in lambda
Cosa sta succedendo? Questo caso particolare è la ragione per una delle più grandi verruche (IMO) in Haskell, il metodo fail
della classe. Nella notazione, quando un pattern match fallisce, viene chiamato fail
. Una traduzione reale sarebbe quello più vicino a:
g' :: Num b => [Maybe b] -> [b]
g' x = x >>= \x' -> case x' of
Just n -> [n+1]
_ -> fail "pattern match exception"
ora abbiamo
Prelude> g' [Nothing]
[]
fail
s utilità dipende dalla monade. Per gli elenchi, è incredibilmente utile, fondamentalmente facendo in modo che il pattern matching funzioni nelle list comprehensions. È anche molto buono nella monade Maybe
, dal momento che un errore di corrispondenza del modello porterebbe a un calcolo fallito, che è esattamente quando Maybe
dovrebbe essere Nothing
. Per IO
, forse non così tanto, in quanto genera semplicemente un'eccezione di errore utente tramite error
.
Questa è la storia completa.
Vedere anche: [Rapporto Haskell 2010> Espressioni # Espressioni Do] (http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-470003.14) –
Solo per fare i pasticci, si noti che 'getPerson' isn 'una funzione, poiché non ha '->' nella sua firma del tipo; se vuoi un nome più preciso di "valore", andrei con "IO action". Vedi ["Tutto è una funzione" in Haskell?] (Http://conal.net/blog/posts/everything-is-a-function-in-haskell) per ulteriori informazioni. –