2012-04-18 2 views
6

Sagen wir, ich habe folgendes amazin Stück Code geschrieben:„<-“ und die damit verbundenen Werte

func = do 
    a <- Just 5 
    return a 

Es ist ziemlich sinnlos, ich weiß. Hier ist a5, und func gibt Just 5 zurück.

Jetzt habe ich meine ehrfürchtige (noch sinnlos) Funktion umschreiben:

func' = do 
    a <- Nothing 
    return a 

Diese Funktion gibt Nothing, aber was zum Teufel ist a? Es gibt nichts, von einem Nothing Wert zu extrahieren, doch das Programm jammert nicht, wenn ich so etwas tun:

func'' = do 
    a <- Nothing 
    b <- Just 5 
    return $ a+b 

Ich habe gerade eine harte Zeit zu sehen, was tatsächlich passiert. Was ist a? Mit anderen Worten: Was macht <-eigentlich? Wenn man sagt "extrahiert den Wert von der rechten Seite und bindet ihn an die linke Seite", vereinfacht dies offensichtlich die Vereinfachung. Was bekomme ich nicht?

Thanks :)

+1

'<-' wird in' >> = 'übersetzt. Im Fall von "Maybe" Monade, wenn das erste Argument (d. H. Der Teil rechts zu "<-')" Nichts "ist, wird nichts anderes ausgewertet und' >> = 'gibt nur' Nothing' zurück. Also, um Ihre Frage zu beantworten: Die Ausführung ist nicht einmal _reach_ 'a'. – Vitus

+0

Wenn Sie an Monaden als Container denken, können Sie mit der Do-Notation die Werte (falls vorhanden) in den Monaden zuweisen und dann Funktionen definieren, die auf diese Werte angewendet werden. Aber die Extraktion ist eine Illusion - Funktionen werden innerhalb der Monade angewendet (mit '>> ='), weil es keine allgemeine Möglichkeit gibt, einen Wert aus einer Monade zu extrahieren. Beachten Sie, wie Sie jeden do-Block beenden, indem Sie result _back in die monad_ setzen, oft mit return. Du hattest nie wirklich eine Variable "a", die gleich 5 war. – Nefrubyr

Antwort

11

Lassen Sie uns versuchen, die do-Notation dieses letzte Beispiel desugar.

func'' = Nothing >>= (\a -> Just 5 >>= (\b -> return $ a+b)) 

Nun, mal sehen, wie >> = für Maybe definiert ist. Es ist in der Prelude:

instance Monad Maybe where 
    (Just x) >>= k = k x 
    Nothing >>= k = Nothing 
    return   = Just 
    fail s   = Nothing 

So Nothing >>= foo einfach Nothing

+0

Danke :) Ich denke, ich bin nicht sehr gut darin, do-notation zu entschuldigen ... – Undreren

+1

Jedes foo <- bar folgt der Schema-Leiste >> = (\ foo -> ...). Jetzt können Sie auch sehen, warum am Ende jedes Do-Blocks ein Ausdruck stehen muss, oder einer dieser (möglicherweise verschachtelten) Lambda-Ausdrücke würde einen leeren Funktionskörper haben. Vielleicht hilft dieser Link: http://book.realworldhaskell.org/read/monads.html#monads.do – Sarah

5

Schauen wir uns die Definition von Maybe Monade suchen ist.

instance Monad Maybe where 
    return = Just 

    Just a >>= f = f a 
    Nothing >>= _ = Nothing 

Und desugar der do -Notation in Ihrer Funktion:

func' = 
    Nothing >>= \a -> 
    return a 

Das erste Argument für >>= ist Nothing und aus der obigen Definition können wir sehen, dass >>= einfach ignoriert das zweite Argument. So erhalten wir:

func' = Nothing 

Da die Funktion \a -> ... nie aufgerufen, a wird nie zugewiesen. Die Antwort lautet also: a wird nicht einmal erreicht.


Was Entzuckerung do -Notation, hier eine kurze Skizze, wie es gemacht wird (es gibt eine Vereinfachung ich gemacht habe - Umgang mit fail, dh Muster, die nicht geeignet sind):

do {a; rest} → a >> do rest 

Beachten Sie, dass >> normalerweise in Form von >>= als a >>= \_ -> do rest implementiert wird (dh die zweite Funktion ignoriert das Argument einfach).

do {p <- a; rest} → a >>= \p -> do rest 

do {let x = a; rest} → let x = a in do rest 

Und schließlich:

do {a} = a 

Hier ist ein Beispiel:

main = do 
    name <- getLine 
    let msg = "Hello " ++ name 
    putStrLn msg 
    putStrLn "Good bye!" 

desugars zu:

main = 
    getLine >>= \name -> 
    let msg = "Hello " ++ name in 
    putStrLn msg >> 
    putStrLn "Good bye!" 

Und t o es für diejenigen, komplett machen, die neugierig sind, hier ist die „richtige“ Übersetzung von do {p <- a; rest} (direkt aus Haskell Bericht genommen):

do {pattern <- a; rest} → let ok pattern = do rest 
           ok _  = fail "error message" 
          in a >>= ok 
6

Die Antwort liegt in der Definition der Monad Instanz Maybe:

instance Monad Maybe where 
    (Just x) >>= k  = k x 
    Nothing >>= _  = Nothing 
    (Just _) >> k  = k 
    Nothing >> _  = Nothing 
    return    = Just 

Ihr func'' übersetzt:

Nothing >>= (\a -> (Just 5 >>= (\b -> return (a+b)))) 

aus der Definition von (>>=) können Sie sehen, dass das erste Nothing gerade durch das Ergebnis durchgefädelt wird.

2

Nothing ist nicht wirklich „nichts“, es ist eigentlich ein möglicher Wert von etwas in der Maybe Monade:

data Maybe t = Nothing | Just t 

Das heißt, wenn Sie etwas vom Typ Maybe t für irgendeine Art t haben, kann es haben der Wert Just x (wobei x alles vom Typ t ist) oderNothing; in diesem Sinne Maybe erweitert nur t um einen weiteren möglichen Wert zu haben, Nothing. (Es hat andere Eigenschaften, weil es ein monad ist, aber dass wir nicht wirklich hier betreffen, mit Ausnahme der syntaktischen Zucker von do und <-.)

1

Lassen Sie uns Ihr Beispiel nehmen:

func = do 
    a <- Just 5 
    return a 

Genau wie in andere Programmiersprachen, die man in zwei Teile zerlegen kann, entsprechen "was bisher gemacht wurde" und "was noch zu tun ist". Zum Beispiel können wir die Pause zwischen Just 5 und

a <- ... 
return a 

In vielen gängigen Programmiersprachen machen Sie die Just 5 werden gestopft in die Variable a und der Code weiterhin erwarten.

Haskell macht etwas anderes. Der "Rest des Codes" kann als eine Funktion gedacht werden, die beschreibt, was Sie mit a machen würden, wenn Sie einen Wert hätten, den Sie einfügen könnten. Diese Funktion wird dann auf Just 5 angewendet. Aber es wird nicht direkt angewendet. Es wird angewendet, wobei die Definition von >>= angewendet wird, abhängig vom Typ Ihres Ausdrucks. Für Maybe ist >>= so definiert, dass bei Just X die Funktion "Rest des Codes" auf X angewendet wird.Aber es ist auch so definiert, dass bei Nothing die Funktion "Rest des Codes" einfach ignoriert wird und Nothing zurückgegeben wird.

Wir können nun Ihr weiteres Beispiel

func'' = do 
    a <- Nothing 
    b <- Just 5 
    return $ a+b 

brechen sie in Nothing und interpretieren:

a <- ... 
    b <- Just 5 
    return $ a+b 

Wie ich oben gesagt, kann dieser Code-Block eines zu einem möglichen angewandten Funktion gedacht werden Wert von a. Aber es wird mit Nothing verwendet und in diesem Fall ist >>= definiert, um den "Rest des Codes" zu ignorieren und einfach Nothing zurückgeben. Und das ist das Ergebnis, das du hast.