2017-10-08 3 views
2

Ich werde meine Fragen mit Beispielen am unteren Rand wiederholen.Haskell Monad Flow Verständnis

Wenn ich m oder k sage, bezog ich mich auf die linken und rechten Werte im Funktionsaufruf, m >>= k.

Es gibt diese Unschärfe im Verständnis von Monaden. Wenn m eine Berechnung ist und k ein Lambda-Ausdruck ist, bedeutet das, dass k zwei Zwecken dient, wo es mit Bindungen im do-Block arbeiten kann und sie auch für andere Zwecke in der Monad-Instanzdefinition verwendet werden kann? Wenn ein Wert gebunden wird, bevor k in seinem do-Block ausgewertet wird, werden Werte automatisch an ihn übergeben oder ist die Definition der Monad-Instanzmethoden, wo wir den einzigen Effekt der Propagierung auf k definieren?

Ich bin vielleicht ein bisschen irreführend durch die Beispiele, die ich sehe, die immer das direkt vorhergehende gebundene Element an den Lambda-Ausdruck übergeben, wenn sie Monaden erklären. Selbst wenn die verwendete Notation pure-do-no-lambda ist, ist es immer das letzte gebundene Element, das unter der Haube an das versteckte Lambda übergeben wird. Ist es nun eine schlechte Übung, ein Lambda zu verwenden, das mehr als ein Argument benötigt, und es als k in m >>= k zu verwenden? Oder liege ich falsch, wenn wir annehmen, dass, wenn wir in einem do-Block arbeiten, nur ein Argument an den nächsten "versteckten" Lambda-Ausdruck übergeben wird und dass dieses eine Argument das unmittelbar vorher gebundene Element ist?

Ich werde nun meine Fragen mit Beispielen wiederholen.

do 
    a <- getLine 
    b <- getLine 
    putStrLn $ a ++ b 

a und b sind in IO Behälter auf Werte gebunden, die an der Ausführung getLine zurückgeführt wird. Unter der Haube, welche der folgenden ist, wenn überhaupt, gleichwertig?

getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b) 

OR

getLine >>= \a -> getLine >>= \(a, b) -> putStrLn (a ++ b) 

Wir sehen, dass der Wert in dem IO Behälter getLine extrahiert und auf die Lambda-Ausdruck weitergegeben.

Wenn die erste korrekt ist, würde das nicht zu einem Fehler führen, da a in der Monad-Definition nicht definiert wäre?

Ich zog die zweite aus der Luft. Ich habe keine Beweise gesehen, dass es das sein könnte, aber selbst wenn es nicht die richtige Antwort ist, können wir so etwas tun? Natürlich müssten wir alle Lambda-Ausdrücke, die mit >>= verwendet werden, alle 2-Tupel in diesem Zusammenhang nehmen. Ist dieses Verhalten nicht allein durch unsere >>= Definition bestimmt, zumindest wenn nur-Lambda-No-Do-Notation verwendet wird?

Können wir diesen Lambda-Ausdruck in der Monad-Definition verwenden und etwas übergeben, was dazu führt, dass der Bildschirm erneut gedruckt wird? Sollten wir?

Wenn wir wollten mit putStrLn in der >>= Definition arbeiten, würden wir einige beliebigen Wert k aus, um passieren müssen, um die putStrLn Funktion zu bekommen?

Vielen Dank. Frieden.

Antwort

2

Ich werde Ihnen nur eine Teilantwort geben, da ich nicht verstanden habe, was genau Sie fragen.Das tut mir leid.

do 
    a <- getLine 
    b <- getLine 
    putStrLn $ a ++ b 

entspricht

getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b) 

und das funktioniert, weil

\b -> putStrLn (a ++ b) 

erzeugt eine Funktion, die a vom Anwendungsbereich erfasst sie definiert ist . Wenn das nicht so ist, a im Rahmen dieses Begriffs wird der Compiler über a nicht im Geltungsbereich sein.

die erweitern lassen, dass ein wenig und wieder

\a -> getLine >>= \b -> putStrLn (a ++ b) 

auf einen Blick. Dies definiert eine Funktion, die auf einen Wert angewandt, wenn x das Ergebnis des Ausdrucks zurückgibt

getLine >>= \b -> putStrLn (x ++ b) 

hier die Variable a durch den Wert x ersetzt wurde. So gibt es keinen a mehr, über den man sich Sorgen machen muss.

1

ungezuckert do block wird wie getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b) Sie nicht alle Parameter aus früheren Aufruf übergeben müssen, weil der lokalen Bereiche, wollen wir bezeichnen sie:

getLine >>= 
(!This is first closure! \a -> getLine >>= 
(!This is second, all variables from the first 
closure are available, because in haskell function closure takes outer scope in! \b -> 
putStrLn(a ++ b) !End of the second closure!) !End of the first closure!) 

Und jetzt über Typ (>> =). GHCI druckt als nächstes (>>=) :: Monad m => m a -> (a -> m b) -> m b. So k ist nur ein Lambda, das Monad m mit arbeiten wird, alles, was es tun muss, ist "implementieren Schnittstelle" für >>=.

2

do Notation wie folgt aus:

do 
    a <- getLine 
    b <- getLine 
    putStrLn $ a ++ b 

äquivalent zu

getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b) 

Dies wiederum ist, entspricht

getLine >>= (\a -> getLine >>= \b -> putStrLn (a ++ b)) 

Beachten Sie die zusätzlichen Klammern. Die erste getLine besteht aus einem Lambda-Ausdruck, wobei a das "enthaltene Element" ist. Innerhalb dieses Lambda-Ausdrucks wird ein neuer Ausdruck aufgerufen. Dieser Ausdruck ist

getLine >>= \b -> putStrLn (a ++ b) 

Dieser Ausdruck ist immer noch ‚innen‘ der erste Lambda-Ausdruck, der bedeutet, dass a noch im Gültigkeitsbereich befindet.

Sie können noch mehr Klammern um Ausdrücke setzen, wenn es hilft:

getLine >>= (\a -> getLine >>= (\b -> putStrLn (a ++ b))) 

Diese Halterungen sind völlig überflüssig, aber sie zeigen, wie die verschiedenen Ausdrücke scoped sind. Beachten Sie, dass sowohl a als auch b noch im Geltungsbereich sind, wenn putStrLn (a ++ b) aufgerufen wird.

1

Entspricht die do der folgenden?

getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b) 

Ja, dieser.

Wenn die erste korrekt ist, würde dies nicht zu einem Fehler führen, da a in der Monadedefinition nicht definiert wäre?

Nein, die -> Pfeil Syntax ist rechtsassoziativ:

getLine >>= (\a -> getLine >>= (\b -> putStrLn (a ++ b))) 

Es bildet einen Verschluss, wo a noch im Rahmen ist.

+0

Beachten Sie, dass der Operator '>> =' tatsächlich assoziativ bleibt. Es ist nur so, dass die Lambda-Syntax '\ x -> ...' eine spezielle syntaktische Form ist, die "so viel wie möglich" verschlingt. –

+0

@ K.A.Buhr Oh, du hast vollkommen recht. Bearbeitet. – Bergi