2013-11-24 20 views
6

Posso vedere come usereste un Monad è Haskell per IO - per creare un contenitore attorno al calcolo per questa operazione. È logico che tu possa usare Monads per "collegare i calcoli insieme", come si comporranno le funzioni per un'operazione di flusso di dati.Monade per il controllo del flusso - Sequenza, selezione e iterazione

Quello che sto solo facendo è che è possibile utilizzare Monade per il controllo del flusso. Ora capisco che il flusso di controllo riguardi Sequenza, Selezione e Iterazione. Ora mi sento a mio agio con funzioni di ordine superiore come map, foldl, filter e zipwith/mapcat per eseguire operazioni sugli elenchi.

La mia domanda è: posso eseguire la sequenza, la selezione e l'iterazione con le monadi per ottenere il flusso di controllo? (Felice per una risposta in Haskell, Scala o Clojure)

risposta

5

per il sequenziamento in Haskell, si hanno le funzioni >>= e sequence:

(>>=) :: Monad m => m a -> (a -> m b) -> m b 
sequence :: Monad m => [m a] -> m [a] 

La funzione >>= o BIND ha un'azione monadica, estrae il valore da esso e lo nutre in una funzione che restituisce una nuova azione monadica. La funzione sequence accetta un elenco di azioni monadiche dello stesso tipo e le esegue tutte, aggregando i risultati e avvolgendoli in un'unica azione.

per l'iterazione avete mapM e forM (forM = flip mapM)

mapM :: Monad m => (a -> m b) -> [a] -> m [b] 

I mapM e forM funzioni sono per l'applicazione di una funzione che restituisce un azione per ogni elemento in un elenco, aggregando i risultati come una singola azione.

Per la selezione, presumo che intendiate condizionali, che sono implementati in Haskell come espressioni if-the-else. Possono essere usati direttamente nelle espressioni monadiche allo stesso modo in cui possono essere usati in espressioni pure. Tuttavia, puoi anche utilizzare alcune monadi per eseguire scelte o almeno gestire errori. Il più facile da Grok è la monade Maybe:

data Maybe a = Nothing | Just a 

instance Monad Maybe where 
    return a = Just a 
    (Just a) >>= f = f a 
    Nothing >>= f = Nothing 

Ha un'interfaccia molto semplice implementazione. In sostanza, se provi a sequenziare uno Nothing in qualcos'altro, restituirà Nothing ogni volta. Questo vi dà l'idea del fallimento cortocircuito:

lookup :: Eq a => a -> [(a, b)] -> Maybe b 
-- Looks up a value in a key-value association list 

myFunc :: Int -> [(String, Int)] -> Maybe Int 
myFunc mult assocList = do 
    i <- lookup "foo" assocList 
    j <- lookup "bar" assocList 
    return $ i * mult + j 

Qui, se la ricerca per "foo" non riesce, l'myFunc restituisce immediatamente Nothing. Analogamente, se la ricerca di "bar" non riesce, myFunc restituisce immediatamente Nothing. È solo quando entrambe le ricerche riescono a fare lo myFunc fare qualsiasi calcolo. Questo fornisce una sorta di "gestione degli errori". C'è una monade simile Either a

data Either a b = Left a | Right b 

instance Monad (Either a) where 
    return a = Right a 
    (Right a) >>= f = f a 
    (Left a) >>= f = Left a 

che funziona molto simile, ad eccezione del valore di "fallimento" può portare un po 'di contesto, come ad esempio un messaggio di errore stringa o lo stato del calcolo al punto di errore.

+1

Does '>> =' garantisce davvero il sequenziamento o è solo il comportamento delle monadi più comuni? – kqr

+1

@kqr Non garantisce il sequenziamento della valutazione, se è questo che stai chiedendo.Il motivo è perché l'ordine di valutazione dipende dalla definizione di '(>> =)', e non tutte le implementazioni di '(>> =)' valutano il primo argomento prima del secondo. –

+0

@kqr Non garantisce il sequenziamento, ma è la funzione che ci consente di usare la notazione 'do', che è quella che ho interpretato come una domanda. Poiché bind definisce essenzialmente il punto e virgola (implicito) per ogni azione in una dichiarazione do, ci consente di definire un aspetto del flusso di controllo per un particolare blocco di codice. – bheklilr