2016-08-02 36 views
15

In vielen Fällen ist mir nicht klar, was man erreichen kann, wenn man zwei Monaden mit einem Transformator kombiniert, statt zwei getrennte Monaden zu verwenden. Offensichtlich ist die Verwendung von zwei separaten Monaden ein Ärger und kann eine Notation innerhalb der Do-Notation beinhalten, aber gibt es Fälle, wo es einfach nicht ausdrucksfähig genug ist?Warum unterscheiden sich Monad-Transformatoren von Monaden?

Ein Fall scheint StateT on List zu sein: das Kombinieren von Monaden bringt nicht den richtigen Typ, und wenn Sie den richtigen Typ über einen Stapel von Monaden erhalten wie Bar (wobei Bar a = Writer w (Identität a))), es tut nicht das Richtige

Aber ich hätte gerne ein allgemeineres und technisches Verständnis von genau, was Monade Transformatoren bringen, wenn sie sind und aren ' t notwendig, und warum

Um diese Frage zu stellen etwas mehr konzentriert.

  1. Was ist ein aktuelles Beispiel einer Monade ohne entsprechenden Transformator (dies würde veranschaulichen, was Transformatoren tun können, wenn sie nur Monaden stapeln).
  2. Sind StateT und ContT die nur Transformatoren, die einen Typ nicht entspricht der Zusammensetzung von ihnen mit m geben, für eine zugrunde liegende Monade m (unabhängig davon, welcher Reihenfolge sie zusammengesetzt sind.)

(ich bin nicht interessiert an bestimmten Implementierungsdetails bezüglich der verschiedenen Bibliotheken, sondern eher an der allgemeinen (und wahrscheinlich Haskell unabhängigen) Frage, was monad Transformatoren/Morphismen als Alternative zum Kombinieren von Effekten hinzufügen, indem man eine Menge monadischer Konstruktoren stapelt.)

(Um ein wenig Kontext zu geben, bin ich ein Linguist, der ein Projekt tut, um Montague Grammatik zu bereichern - hat einfach Lambda-Kalkül für compos eingegeben Wortbedeutungen in Sätze - mit einem Monad-Transformatorstapel. Es wäre wirklich hilfreich sein, zu verstehen, ob Transformatoren tun etwas wirklich nützlich für mich.)

Danke,

Reuben

+1

Was meinen Sie mit "zwei getrennte Monaden verwenden"? Kannst du ein Beispiel geben? – ErikR

+0

Mögliches Duplikat von [mtl, Transformatoren, Monaden-fd, MonadLib und dem Paradox der Wahl] (http://stackoverflow.com/questions/2769487/mtl-transformers-monads-fd-monadlib-and-the-paradox- der Wahl) –

+0

Ich habe gewählt, um zu schließen, weil es wirklich klingt, als ob Sie für den Unterschied zwischen verschiedenen Monade Transformer-Bibliotheken fragen, und diese Frage hat bereits eine ausgezeichnete Antwort. Wenn Sie denken, dass Ihre Frage anders ist, könnten Sie vielleicht näher darauf eingehen. –

Antwort

17

zu beantworten, die Sie über den Unterschied in Frage zwischen Writer w (Maybe a) vs MaybeT (Writer w) a, lassen Sie uns zunächst einen Blick auf die Definitionen unter:

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) } 
type Writer w = WriterT w Identity 

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } 

Mit ~~ bedeutet "strukturell ähnlich" wir haben:

Writer w (Maybe a) == WriterT w Identity (Maybe a) 
        ~~ Identity (Maybe a, w) 
        ~~ (Maybe a, w) 

MaybeT (Writer w) a ~~ (Writer w) (Maybe a) 
        == Writer w (Maybe a) 
        ... same derivation as above ... 
        ~~ (Maybe a, w) 

In einem gewissen Sinne sind Sie richtig - strukturell sowohl Writer w (Maybe a) und MaybeT (Writer w) a gleich sind - beide sind im Wesentlichen nur ein Paar von einem Vielleicht Wert und ein w.

Der Unterschied ist, wie wir sie als monadische Werte behandeln. Die return und Klassenfunktionen tun sehr unterschiedliche Dinge je , auf denen Monad sie Teil sind.

Betrachten wir das Paar (Just 3, []::[String]). Mit Hilfe der Vereinigung wir abgeleitet haben oben hier, wie das Paar würde in beiden Monaden ausgedrückt werden:

three_W :: Writer String (Maybe Int) 
three_W = return (Just 3) 

three_M :: MaybeT (Writer String) Int 
three_M = return 3 

Und hier ist, wie wir das Paar (Nothing, []) bauen würde: auf

nutin_W :: Writer String (Maybe Int) 
nutin_W = return Nothing 

nutin_M :: MaybeT (Writer String) Int 
nutin_M = MaybeT (return Nothing) -- could also use mzero 

Betrachten wir nun diese Funktion Paare:

add1 :: (Maybe Int, String) -> (Maybe Int, String) 
add1 (Nothing, w) = (Nothing w) 
add1 (Just x, w) = (Just (x+1), w) 

und lassen sie uns sehen, wie wir es in den zwei verschiedenen Monaden implementieren würde:

add1_W :: Writer String (Maybe Int) -> Writer String (Maybe Int) 
add1_W e = do x <- e 
      case x of 
       Nothing -> return Nothing 
       Just y -> return (Just (y+1)) 

add1_M :: MaybeT (Writer String) Int -> MaybeT (Writer String) Int 
add1_M e = do x <- e; return (e+1) 
    -- also could use: fmap (+1) e 

Im Allgemeinen werden Sie sehen, dass der Code in der MaybeT Monade prägnanter ist.

Darüber hinaus semantisch die beiden Monaden sind sehr unterschiedlich ...

MaybeT (Writer w) a ist ein Writer-Aktion, die ausfallen können, und der Fehler ist automatisch für Sie behandelt. Writer w (Maybe a) ist nur ein Writer Aktion, die eine Maybe zurückgibt. Nichts Besonderes passiert, wenn der Wert sich als Nichts herausstellt. Dies wird in der add1_W-Funktion veranschaulicht, wobei eine Fallanalyse unter x durchzuführen war.

Ein weiterer Grund, den MaybeT Ansatz zu bevorzugen, ist, dass wir Code schreiben können, der generisch über jedem Monadstapel ist. Zum Beispiel kann die Funktion:

square x = do tell ("computing the square of " ++ show x) 
       return (x*x) 

unverändert in jedem Monadstack verwendet werden, der einen Writer String, z.B.:

WriterT String IO 
ReaderT (WriterT String Maybe) 
MaybeT (Writer String) 
StateT (WriterT String (ReaderT Char IO)) 
... 

Aber der Rückgabewert von square nicht geben gegen Writer String (Maybe Int) nicht überprüfen, weil square keine Maybe zurück.

Wenn Sie in Writer String (Maybe Int) codieren, enthüllt Code explizit die Struktur von Monad, die es weniger generisch macht. Diese Definition von add1_W:

add1_W e = do x <- e 
       return $ do 
       y <- x 
       return $ y + 1 

funktioniert nur in einem zweischichtigen Monade Stapeln während einer Funktion wie square Arbeiten in einem viel allgemeineren Rahmen.

+0

Danke, das ist hilfreich. Aber ich denke meine Frage steht immer noch.Angenommen, für add1_W, Sie schrieb folgenden: add1_W e = do x <- e return $ tut y <- x return $ y + 1 Dies ist immer noch mehr ein Streit als add1_M, aber nicht erfordern alle case-Anweisungen und verwenden sowohl die may- als auch die writer-Monade. – Reuben

+1

Antwort aktualisiert. – ErikR

+0

Eine kleine Korrektur: in add1_M glaube ich du meinst 'return (x + 1)'. – Ryan

6

Was ohne entsprechenden Transformator ein tatsächliches Beispiel einer Monade ist (dies würde Hilfe veranschaulichen, was Transformatoren tun können, nur Monaden stapeln kann nicht).

IO und ST sind die kanonischen Beispiele hier.

Sind StateT und ContT die einzigen Transformatoren, die einen Typ geben, der nicht der Zusammensetzung von ihnen mit m entspricht, für eine zugrundeliegende Monade m (unabhängig davon, aus welcher Reihenfolge sie zusammengesetzt sind).

)

Nein, ListT m a ist nicht (isomorph) [m a]:

newtype ListT m a = 
    ListT { unListT :: m (Maybe (a, ListT m a)) } 
+1

Interessant zu bemerken, dass es immer noch fast die Komposition ist, Sie müssen nur die Komposition unter dem Fixpunkt verschieben: 'Liste a = Fix (1 + a * x)' und 'ListT ma = Fix (m ∘ (1 + a * x)) ' – Cactus