2015-01-04 4 views
8

Ich bin ein sehr neuer Haskell Lerner. Ich habe einen Arbeits Ausdruck:Haskell Expression Equivalents

do x <- try parseA <|> parseB 
    return x 

, die perfekt zu funktionieren scheint (Ich bin das Parsec Paket, aber ich hoffe, dass diese Frage hat nichts mit seiner Funktionalität zu tun, soweit ich weiß, <|> ist ein Parsec- definierter Infix-Operator). parseA und parseB haben beide den Parser Foo Monad-Typ, ebenso wie der gesamte Ausdruck.

Nach dem, was ich bisher gelesen habe, scheint es diese

do return (try parseA <|> parseB) 

und

do return $ try parseA <|> parseB 

aber keines der letzteren Kompilierung gleichwertig sein sollte, sie klagen über nicht übereinstimmen Typen (Fehler unten).

Mein anderer Versuch, dies zu umschreiben, als

(try parseA <|> parse B) >>= return 

scheint zu funktionieren. Aber wenn ich das auch falsch verstanden habe, sag es bitte.

Also meine Frage ist, kann jemand erklären, warum die ersten drei unterschiedlich sind. Ich bin verwirrt, warum sie nicht gleichwertig sind. Was vermisse ich?


Fehler (falls dies relevant ist, obwohl FWIW ich nicht bin auf der Suche nach meinem Code ‚fix‘ - Ich habe eine funktionierende Version, ich bin auf der Suche zu verstehen, wie die Versionen unterscheiden):

do return (try parseA <|> parseB) 

gibt

parse.hs:76:11: 
    Couldn't match expected type ‘Foo’ 
       with actual type ‘Text.Parsec.Prim.ParsecT 
            [Char]() Data.Functor.Identity.Identity Foo’ 

und

do return $ try parseA <|> parseB 

gibt

parse.hs:76:3: 
    Couldn't match type ‘Text.Parsec.Prim.ParsecT 
          [Char]() Data.Functor.Identity.Identity Foo’ 
        with ‘Foo’ 
    Expected type: Parser Foo 
     Actual type: Text.Parsec.Prim.ParsecT 
        String 
        () 
        Data.Functor.Identity.Identity 
        (Text.Parsec.Prim.ParsecT 
         [Char]() Data.Functor.Identity.Identity Foo) 

Antwort

11

Do Notation Entzuckern

Die rules for do notation desugaring implizieren, dass

do x <- try parseA <|> parseB 
    return x 

zu

entspricht
(try parseA <|> parse B) >>= return 

($) gegen (>>=)

Seit ist eine blätterte Version von (>>=), beide

gleichwertig sind
return =<< (try parseA <|> parse B) 

Dies bedeutet, dass der einzige Unterschied zwischen Ihrer korrekten Version und return $ try parseA <|> parse B ist der Unterschied zwischen und ($), deren Typen:

($) :: (a -> b) -> a -> b 
(=<<) :: (a -> m b) -> m a -> m b 

Sie können sehen, dass ($) ist kein Ersatz für , aber vielleicht können Sie auch sehen, dass sie etwas ähnlich sind. Ein Weg, um es zu betrachten ist, dass – und damit auch (>>=) – ist eine Art von Funktion Anwendung, die „monadischen Funktionen“ vom Typ a -> m b für einige Monad m auf „monadischen Werte“ vom Typ m a für einige Monad gilt m, während ($) ist die übliche Art von Funktionsanwendung, die Funktionen des Typs a -> b auf Werte des Typs a anwendet.

Monad Gesetze

Einer der monad laws ist, dass

k >>= return = k 

Dies bedeutet, dass

(try parseA <|> parse B) >>= return 

kann auch als

try parseA <|> parse B 

geschrieben werden, die bedeutet, dass Sie Die ursprüngliche Form, die do-Notation verwendet, kann auch auf diese Weise geschrieben werden.

+4

Danke, das war der Tipp, den ich verstehen musste. Das andere Bit, das mir fehlte, ist das <- ist nicht nur eine Zuweisung, es entfernt die Monade und gibt den zugrundeliegenden Typ zurück, also x <- y dann ist foo x nicht äquivalent zu foo y.Ich schätze die umfassende Antwort, Rein. – Ian