2013-11-24 8 views
6

Ich kann sehen, wie Sie eine Monade verwenden würden ist Haskell für IO - um einen Container um die Berechnung für diesen Vorgang zu erstellen. Es macht Sinn, dass Sie Monaden verwenden können, um Berechnungen miteinander zu verbinden - so wie Sie Funktionen für eine Datenflussoperation zusammenstellen würden.Monaden für Kontrollfluss - Reihenfolge, Auswahl und Iteration

Was ich gerade nur grokking bin, ist, dass Sie Monaden für Control Flow verwenden können. Jetzt verstehe ich Kontrollfluss über Sequenz, Auswahl und Iteration. Jetzt bin ich mit höherwertigen Funktionen wie map, foldl, filter und zipwith/mapcat vertraut, um Operationen auf Listen durchzuführen.

Meine Frage ist - kann ich Sequenz, Auswahl und Iteration mit Monaden tun, um Kontrollfluss zu erreichen? (glücklich für eine Antwort in Haskell, Scala oder Clojure)

Antwort

5

Für die Sequenzierung in Haskell, haben Sie die Funktionen >>= und sequence:

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

Die >>= oder bind Funktion nimmt eine monadischen Aktion, extrahiert den Wert daraus und füttert es in eine Funktion, die eine neue monadische Aktion zurückgibt. Die sequence-Funktion nimmt eine Liste von monadischen Aktionen des gleichen Typs und führt alle von ihnen aus, aggregiert ihre Ergebnisse und wickelt sie als eine einzige Aktion ein.

Für Iteration haben Sie mapM und forM (forM = flip mapM)

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

Die mapM und forM Funktionen sind für eine Funktion der Anwendung, die eine Aktion für jedes Element in einer Liste zurückgibt, die Ergebnisse als eine einzige Aktion aggregieren.

Zur Auswahl nehme ich an, Sie meinen Bedingungen, die in Haskell als nur If-the-else-Ausdrücke implementiert sind. Sie können direkt in monadischen Ausdrücken verwendet werden, genauso wie sie in reinen Ausdrücken verwendet werden können. Sie können jedoch auch bestimmte Monaden zum Ausführen von Auswahlmöglichkeiten oder zumindest zur Behandlung von Fehlern verwenden. Der einfachste grok ist die Maybe Monade:

data Maybe a = Nothing | Just a 

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

Es hat eine sehr einfache Implementierung hat. Im Wesentlichen, wenn Sie versuchen, eine Nothing in etwas anderes zu sequenzieren, wird es Nothing jedes Mal zurückgeben. Dies gibt Ihnen die Vorstellung von kurzgeschlossen failure:

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 

Wenn hier die Suche für "foo" ausfällt, die myFunc sofort wieder Nothing. Wenn die Suche nach "bar" fehlschlägt, gibt myFunc sofort Nothing zurück. Nur wenn beide Nachschlagevorgänge erfolgreich sind, führt myFunc eine Berechnung durch. Dies bietet eine Art "Fehlerbehandlung". Es gibt eine ähnliche Monade 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 

, die sehr ähnlich, mit Ausnahme des „Fehler“ Wert arbeitet, kann etwas Kontext tragen, wie zum Beispiel einer String Fehlermeldung oder den Zustand der Berechnung an dem Punkt des Scheiterns.

+1

Ist '>> =' wirklich garantiert Sequenzierung oder ist das nur das Verhalten der häufigsten Monaden? – kqr

+1

@kqr Es garantiert nicht Sequenzierung der Auswertung, wenn das ist, was Sie fragen.Der Grund dafür ist, weil die Reihenfolge der Auswertung von der Definition von '(>> =)' abhängt, und nicht alle Implementierungen von '(>> =)' das erste Argument vor dem zweiten Argument auswerten. –

+0

@ kqr Es garantiert keine Sequenzierung, aber es ist die Funktion, die es uns erlaubt, die 'do'-Notation zu verwenden, was ich als Frage interpretiert habe. Da bind im Prinzip das (implizierte) Semikolon für jede Aktion in einer do-Anweisung definiert, gibt es uns die Möglichkeit, einen Aspekt des Kontrollflusses für einen bestimmten Codeblock zu definieren. – bheklilr