2016-02-24 5 views
8

Ich versuche gerade zu lernen Haskell und ich kann wirklich nicht verstehen, das Konzept der Verwendung nur einer Monade in Do-Block. Wenn ich foo :: Int -> Maybe Int habe und in dieser Funktion zB die Funktion hIsEOF :: Handle -> IO Bool verwenden möchte. Kann mir bitte jemand ein einfaches Beispiel erklären, wie würde ich verwenden und könnte irgendwie mit Bool arbeiten?Mehrere Monaden in einem Do-Block

Ich habe versucht, hier und auf Google zu suchen, aber ich stoße immer auf etwas fortgeschrittenes Zeug, wo im Grunde niemand erklärt, wie, sie geben nur Ratschläge, wie man es richtig in OP-Code passt. Ich habe Monadetransformatoren in diesen Threads erwähnt, aber selbst nachdem ich nur wenige Ressourcen gelesen habe, kann ich nicht den richtigen Weg finden, wie man sie benutzt.

Antwort

7

Die kurze Antwort ist nein. do-Notation basiert auf zwei Dinge

return :: a -> m a 
>>= :: m a -> (a -> m b) -> m b 

Hinweis in >>=, dass Sie zwar mit zwei unterschiedlichen Innen Typen arbeiten können (a und b), es nur mit einem äußeren Typ arbeitet, eine Monade (m). Sowohl m a als auch sind die gleichen Monade.

Die längere Antwort ist, Sie müssen sie in die gleiche Monade konvertieren. Zum Beispiel kann Maybe-IO wie so umgewandelt werden:

maybeToIO Nothing = error "No thing" 
maybeToIO (Just a) = return a 

Monaden, in der Regel kann nicht ineinander obwohl umgewandelt werden, außer in besonderen Fällen.


Warum also >>= nur mit einer Monade arbeiten? Schauen Sie sich einfach this an. Es ist definiert so mit einer einzigen Monade zu einer Zeit zu arbeiten, und Do-Notation ist definiert mit >>= zu arbeiten. Die Gründe, warum diese Definition gewählt wurde, sind etwas kompliziert, aber ich kann sie bearbeiten, wenn jemand möchte.

Sie könnten Ihre eigene >>= erstellen, die mit mehreren Monaden funktioniert, und dann rebindable syntax verwenden, aber das wäre wahrscheinlich schwer.

+0

Danke. Also Schlussfolgerung ist, dass ich etwas zwischen ihnen konvertieren muss. Funktioniert sie wie Ihre 'thobenToIO'-Methode und ist eine gute Übung im Haskell-Code? Ich muss vor kurzem ein Projekt in Haskell machen und möchte keinen Spaghetti-Code produzieren. Außerdem habe ich kürzlich herausgefunden, dass etwas wie 'foo :: Int -> IO (Maybe Int)' scheint irgendwie für mich zu arbeiten, aber ist es eine schlechte Sache zu tun? Ist es hässlich? –

+2

Es kann auch helfen, zu sehen, wie die Notation in die standardmäßigen monadischen Operatoren entpackt wird. https://en.wikibooks.org/wiki/Haskell/Syntactic_sugar#Do_notation – chepner

+0

@ MarekMilkovič Ich würde nur für IO sagen, da IO sowieso fehleranfällig ist. Im Allgemeinen muss jede Monadenumwandlung anders durchgeführt werden und ist manchmal unmöglich. Außerdem werden Sie feststellen, dass 'foo' nicht so nützlich ist (insbesondere die meisten Anwendungen von' foo' werden einen besseren Weg haben, dies zu tun.) – PyRulez

11

Mit Monade Transformatoren, alles, was Sie tun müssen, ist

  1. die Funktion Signatur Wechsel von Int -> Maybe Int zu

  2. lift alle IO Aktionen innerhalb des do Block (oder liftIO in dieser Fall). Siehe here why you need this lifting and what exactly it does.

  3. laufen die Funktion runMaybeT

ein minimales Beispiel unter Verwendung wäre:

dann,

\> runMaybeT $ foo 1 
Just 1 
\> runMaybeT $ foo 100 -- would hit eof 
Nothing 

, was Sie daraus herauskommen wäre von der Art :

(runMaybeT . foo) :: Int -> IO (Maybe Int) 
+0

Danke. Ich fange an, das Konzept zu verstehen, aber ich habe eine Frage. Ist dies ein bevorzugter Weg, um das zu tun, was ich brauche, oder sollte ich mein Design überdenken? Es ist nur so, dass es für mich so aussieht, als müsste ich '-> IO a' in fast jeder einzelnen Funktion haben. –

+0

@ MarekMilkovič Es hängt davon ab, wie Sie Fehler propagieren wollen (wollen Sie Ausnahmen verwenden, oder 'Nichts ist.) – PyRulez

+2

@ MarekMilkovič im Idealfall nur sehr wenige kleine Funktionen sollten IO tun. Alles andere sollte rein sein –