2015-06-07 9 views
13

Die folgenden Codes sieht ganz klar:Was ist der Typ der Variable in Do-Notation hier in Haskell?

do 
    x <- Just 3 
    y <- Just "!" 
    Just (show x ++ y) 

Hier ist die Art von x ist Num und yString ist. (<- hier verwendet, aus dem Monad Istwert zu nehmen)

Doch diese Schnipsel mir nicht so klar sehen:

import Control.Monad.Instances 
addStuff :: Int -> Int 
addStuff = do 
    a <- (* 2) 
    b <- (+ 10) 
    return (a + b) 

Was ist die Art von a und Art des b hier? Es scheint, sie verhalten sich wie ein Num, aber a <- (* 2) und b <- (+ 10) sieht kryptisch hier ...

Hat jemand Ideen dazu?

+5

'<-' * Gilt nicht * 'entfernen', um den Wert von einer Monade erhalten.Es ist der syntaktische Zucker, der * einen Wert herauszieht, aber tatsächlich nur auf eine bestimmte Weise manipuliert. Aus diesem Grund können Sie Funktionen als Monaden behandeln, da Sie zwar das Ergebnis einer Funktion nicht buchstäblich "herausziehen" können, aber Funktionen zusammenführen können, um eine andere zu erstellen. – AJFarmar

Antwort

16

Nun, Sie sind auf eine Art seltsamer Monade gestoßen.

Die fragliche Monade ist die Monad ((->) r). Nun, was bedeutet das? Nun, es ist die Monade von Funktionen des Formulars r -> *. D. h., Von Funktionen, die die gleiche Art von Eingabe nehmen.

Sie haben gefragt, was der Typ a und b in diesem Fall sind. Nun, sie sind beide Num a => a, aber das erklärt nicht wirklich viel.

Intuitiv können wir die Monade wie folgt verstehen: Ein monadischer Wert ist eine Funktion, die einen Wert vom Typ r als Eingabe nimmt. Immer wenn wir in der Monade binden, nehmen wir diesen Wert und übergeben ihn an die gebundene Funktion.

Das heißt, in unserem addStuff Beispiel, wenn wir addStuff 5 nennen, dann ist a-(*2) 5 gebunden (was 10 ist) und b auf (+10) 5 (die 15 ist) gebunden.

Lasst uns ein einfacheres Beispiel aus dieser Monade sehen, um zu versuchen zu verstehen, wie es genau funktioniert:

mutate = do a <- (*2) 
      return (a + 5) 

Wenn wir desugar dies zu einer Bindung, erhalten wir:

mutate = (*2) >>= (\a -> return (a + 5)) 

Nun, dies doesn ‚t viel helfen, so ist für diese Monade verwenden the definition of bind lassen:

mutate = \ r -> (\a -> return (a + 5)) ((*2) r) r 

Dies reduziert auf

mutate = \ r -> return ((r*2) + 5) r 

, die wir die Definition verwenden, dass returnconst kann zu

mutate = \ r -> (r*2) + 5 

reduzieren, die eine Funktion ist, die eine Zahl von 2 multipliziert und addiert dann 5.

dass jemandes komische Monade.

+2

Vielleicht möchten Sie die Monade (Reader) so benennen, dass der Fragesteller nach Ressourcen darüber suchen kann. :) – Sarah

+0

Ich würde es nicht eine 'wierd' Monade nennen; es ist sicherlich schwierig, zuerst den Kopf herumzukriegen, aber es ist meines Erachtens das perfekte Beispiel für eine Monade als unerreichbare Berechnung oder Berechnungskontext. – AJFarmar

9

Bei addStuff

addStuff :: Int -> Int 
addStuff = do 
    a<-(*2) 
    b<-(+10) 
    return (a+b) 

die Definition desugars in

addStuff = 
    (* 2) >>= \a -> 
     (+ 10) >>= \b -> 
      return (a + b) 

Bewegen der Maus über die >>= in fpcomplete online editor zeigt

:: Monad m => forall a b. 
       (m a  ) -> (a -> m b  ) -> (m b  ) 
::   (Int -> a ) -> (a -> Int -> b ) -> (Int -> b ) 
::   (Int -> Int) -> (Int -> Int -> Int) -> (Int -> Int) 

Das führt uns verwenden wir eine Monade Instanz für Funktionen führt zu glauben, . In der Tat, wenn wir an der source code betrachten, sehen wir

instance Monad ((->) r) where 
    return = const 
    f >>= k = \ r -> k (f r) r 

diese neu gewonnenen Informationen verwenden, können wir die addStuff Funktion selbst bewerten.

der anfänglichen Ausdruck

(* 2) >>= (\a -> (+10) >>= \b -> return (a + b)) 

wir die >>= Definition ersetzen Verwendung gegeben, uns zu geben (im folgenden {}, [], () unterschiedliche Tiefe von () nur illustrieren)

\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {(* 2) r1} r1 

vereinfachen

die zweite -letzter Begriff innerhalb des äußersten Lambdas

\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {r1 * 2} r1 

gelten {r1 * 2} zu {\a -> ...}

\r1 -> {(+10) >>= \b -> return ((r1 * 2) + b)} r1 

Ersatz verbleibende >>= mit ihrer Definition wieder

\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [(+10) r2] r2} r1 

vereinfachen zweiten bis letzten Begriff innerhalb des inneren lambda

\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [r2 + 10] r2} r1 

gelten [r2 + 10] bis 012.

\r1 -> {\r2 -> [return (r1 * 2 + (r2 + 10))] r2} r1 

gelten r1 zu {\r2 -> ...}

\r1 -> {return (r1 * 2 + r1 + 10) r1} 

Ersatz return mit seiner Definition

\r1 -> {const (r1 * 2 + r1 + 10) r1} 

const x _ = x

\r1 -> {r1 * 2 + r1 + 10} 
012 bewerten

prettify

\x -> 3 * x + 10 

schließlich wir

addStuff :: Int -> Int 
addStuff = (+ 10) . (* 3) 
+0

Schöne Reduktion! Tolle Arbeit, sehr informativ. – AJFarmar