Monaden gegen Pfeile

70

Ich bin mit den Konzepten von Monaden und Pfeilen, wie sie in der funktionalen Programmierung verwendet werden, weitgehend vertraut . Ich verstehe auch, dass sie verwendet werden können, um ähnliche Probleme zu lösen.

Ich bin jedoch immer noch etwas verwirrt darüber, wie ich auswählen soll, welches in einer bestimmten Situation verwendet werden soll.

Wann sollte ich Monaden verwenden und wann sollte ich Pfeile verwenden?

mikera
quelle
3
Laut dem von Ihnen geposteten Wikipedia-Link sind Monaden eine Art Pfeil.
Michael Mior

Antworten:

77

Es gibt zwei ausgezeichnete Artikel von Lindley, Wadler & Yallop ( hier an der LTU besprochen ).

Das Wichtigste zu verstehen ist, dass es mehr Dinge gibt, die Pfeile sind, als Dinge, die Monaden sind. Umgekehrt sind Monaden streng stärker als Pfeile (das zweite Papier oben gibt genau an, auf welche Weise).

Insbesondere sind Monaden Pfeile, die mit einer Apply-Funktion vom Typ ausgestattet sind (a ~> b, a) ~> b, wobei (~>)der Konstruktor für einen bestimmten Pfeil ist. Lindley et al. Weisen Sie darauf hin, dass dies die sorgfältige Unterscheidung zerstört, die Pfeile zwischen Begriffen und Befehlen (oder, wenn Sie es vorziehen, Objekten und Morphismen) beibehalten.

Anwendbare Funktoren haben eine Vielzahl von Anwendungen, insbesondere für Dinge, die am besten als Operationen an Streams betrachtet werden. Man kann sich tatsächlich Pfeile vorstellen, die sich aus der Verallgemeinerung des Begriffs eines Transformators auf Strömen ergeben (dh die Einführung einer neuen Sprache für Morphismen auf Objekten, die von einem bestimmten anwendbaren Funktor konstruiert wurden).

Nach meiner Erfahrung ist ein Begriff in einer Monade im Allgemeinen weitaus notwendiger undurchsichtig als ein Begriff in, da Monaden die Unterscheidung zwischen Objekten und Morphismen verwischen (dh wenn ich die Wörter richtig verwende, was zu einer geschlossenen kartesischen Kategorie führt) ein Pfeil oder applicative Funktors (obwohl beachten Sie, dass beide können Sie injizieren beliebige Funktionen durch die arrund pureMethoden respectively).

Wenn also etwas nicht die Eigenschaften einer Monade erhält (obwohl es konzeptionell eine bildet), kann es möglicherweise genauer untersucht und optimiert werden. Es ist möglicherweise auch einfacher zu serialisieren. Daher die Verwendung von Applikativen und Pfeilen bei Parsern und Schaltungsmodellen.


Das Obige versuchte allgemein und beschreibend zu sein. Nachfolgend einige meiner Faustregeln.

Wenn Sie etwas modellieren müssen, das wie ein Zustand aussieht, beginnen Sie mit einer Monade. Wenn Sie etwas modellieren müssen, das wie ein globaler Kontrollfluss aussieht (dh Ausnahmen, Fortsetzungen), beginnen Sie mit einer Monade. Wenn eine Anforderung auftritt, die mit der Macht und Allgemeinheit von Monaden in Konflikt steht (dh für die der Beitritt (join :: m (m a) -> m a)zu mächtig ist), sollten Sie in Betracht ziehen, die Macht der von Ihnen verwendeten Sache zu vernachlässigen.

Wenn Sie Streams und Transformationen in Streams modellieren müssen, insbesondere Streams, für die bestimmte Merkmale (insbesondere unbegrenzte Ansichten der Vergangenheit und Zukunft) undurchsichtig sein sollten, beginnen Sie mit einem anwendbaren Funktor. Wenn Sie genauere Überlegungen zu den Eigenschaften von Transformationen in Streams benötigen, sollten Sie nach einem Pfeil greifen.

Oder, grob gesagt, Anwendungen gelten für die Aktionen von Schaltkreisen, Pfeile für die Strukturen von Schaltkreisen und Monaden für allgemeine Berechnungseffekte.

Die Geschichte hat natürlich noch viel mehr zu bieten. Für Anwendungen siehe insbesondere Conal Elliotts Arbeit zu FRP . Pfeile finden Sie unter anderem in der HXT XML-Parser-Bibliothek , im Yampa FRP-Projekt , im Haskell on a Horse-Webframework , in Hudaks und Lius klassischem Papier "Plugging a Space Leak with a Arrow" . Für Monaden überall sehen. Und natürlich beachten Sie, dass nur weil etwas eine Monade ist, dies nicht bedeutet, dass die anwendbare Notation möglicherweise nicht klarer und aussagekräftiger ist.

sclv
quelle
Vielen Dank - sehr nützlich und toll, Ihre Meinung zu hören!
Mikera
2
Ich habe wegen Informationsüberlauf heruntergestimmt. Die Antwort erzeugte mehr Jargon als klare Führung, was mich noch verlorener und ängstlicher vor Pfeilen machte. Die direkte Antwort von Dimitrios ist viel mehr eine klare Anleitung als diese (keine Beleidigung!)
user5193682
@ user5193682 Diese Antwort ist einfach falsch .
Will Ness
15

Die kurze Antwort lautet, dass Pfeile allgemeiner als Monaden sind und auch umständlicher zu verwenden sind. Daher sollten Sie Monaden verwenden, wann immer Sie können, und die Verwendung von Pfeilen für die Fälle beibehalten, in denen Monaden nicht anwendbar sind.

Die Antwort „Landschaftsroute“ folgt.

John Hughes, der Arrows vorgestellt hat, hat zwei großartige Artikel veröffentlicht, die ich empfehle: "Verallgemeinerung von Monaden auf Pfeile" und "Programmieren mit Pfeilen" . Diese beiden Artikel sind leicht zu lesen und geben die Antwort auf Ihre Frage. Auch wenn einige Leute nicht alle Details oder den Code in diesen beiden Artikeln verstehen, werden sie sicherlich viele Informationen und sehr hilfreiche Erklärungen zu Monaden und Pfeilen finden.

Ich werde nun die wichtigsten Punkte aus diesen Artikeln hervorheben, die sich auf Ihre Frage beziehen

Als Monaden eingeführt wurden, dachten die Leute, sie seien allmächtig. In der Tat packen Monaden viel Kraft. Aber irgendwann wurde festgestellt, dass es Fälle gibt, in denen Monaden nicht angewendet werden können. Diese Fälle haben mit mehreren Eingängen zu tun, insbesondere wenn einige der Eingänge statisch und einige der Eingänge dynamisch sind. Also trat John Hughes vor und stellte Arrows vor.

Pfeile sind allgemeiner als Monaden. Pfeile sind eine Obermenge von Monaden. Sie können alles, was Monaden tun und noch mehr. Sie sind aber auch schwieriger zu bedienen. John Hughes empfiehlt, dass Sie Monaden verwenden sollten, wann immer Sie können, und dass Sie Pfeile verwenden sollten, wenn Sie keine Monaden verwenden können.

Ich stimme John Hughes zu. Ich erinnere mich auch an Einsteins Zitat „Alles sollte so einfach wie möglich gemacht werden, aber nicht einfacher“.

Natürlich hängt alles von der jeweiligen Situation ab. Lassen Sie mich erklären. Nehmen wir an, Sie lernen Haskell. Dann wäre es eine großartige Aufgabe, jedes Programm mit einem monadischen Ansatz auszuführen und es mit einem pfeilbasierten Ansatz zu wiederholen. Wenn Sie lernen, sollten Sie sich bemühen, alle Möglichkeiten zu erkunden und alle Arten von Ansätzen umzusetzen. Auf diese Weise erhalten Sie großartige Einblicke und können verschiedene Lösungen aus erster Hand vergleichen.

Nehmen wir nun an, Sie möchten der Community eine Bibliothek zur Verfügung stellen. Nun, Sie sind es den Leuten schuldig, die Ihren Code lesen, dass Sie den Ansatz verwenden sollten, der am einfachsten zu verstehen ist und dennoch die Arbeit erledigt. Sie sind es auch den Personen schuldig, die Ihren Code verwenden, dass Ihre Lösung nicht unnötig komplex ist. Auf diese Weise ist Ihre Lösung einfacher zu warten und weniger anfällig für Fehler und Bugs.

Aber was ist, wenn Sie sich in einem Grenzfall befinden? Nehmen wir an, Sie sind sich nicht sicher, ob Sie die zusätzliche Kraft von Arrows benötigen oder nicht. Was solltest du dann tun? Sollten Sie mit einem monadischen Ansatz beginnen und später bei Bedarf zu einem pfeilbasierten wechseln? Oder sollten Sie von Anfang an mit Arrows beginnen und einen kostspieligen Wechsel in der Mitte des Projekts vermeiden?

Wieder ist meine Antwort, den ersten Ansatz zu versuchen: Versuchen Sie, Monaden zu verwenden, wenn Sie können. Wenn Sie später feststellen, dass Sie Monaden nicht verwenden können, müssen Sie einen kostspieligen Wechsel hinnehmen, bei dem Sie das Projekt neu starten und wiederholen müssen, um Arrows verwenden zu können. Dieser Ansatz erfordert sicherlich mehr Zeit und andere Ressourcen von Ihrer Seite. Aber Sie werden wissen, dass Sie das Richtige getan haben, nämlich die einfachste, klarste und weniger komplexe Lösung zu finden.

Das Vermeiden unnötiger Komplexität ist das Wichtigste. Ob Sie es glauben oder nicht, dies ist der Grund, warum Konzepte (wie Funktionszusammensetzung, Monaden und Pfeile) aus der Kategorietheorie in die Informatik eingeführt wurden. Ironisch?

Dimitrios Kalemis
quelle
4
"Pfeile sind allgemeiner als Monaden. Pfeile sind eine Obermenge von Monaden. Sie können alles, was Monaden tun und mehr." Könnten Sie bitte näher erläutern, was Sie im dritten Satz damit meinen? Der Satz scheint den ersten beiden zu widersprechen. Funktoren sind Obermengen von Monaden und meiner Meinung nach können sie weniger (dh nur fmap) als Monaden.
Jhegedus
Ich kann sehen, warum Sie verwirrt sind. Das Problem ist, dass ich das Wort "allgemein" und den Begriff "Obermenge" ziemlich locker verwende. Damit ich verstanden werde, sage ich: Funktoren können weniger als Monaden. Monaden können weniger als Pfeile. Infolgedessen sind Funktoren am wenigsten mächtig, Monaden haben mehr Kraft und Pfeile die meiste Kraft. Und hier meine ich mit dem Wort "Macht" "Fähigkeit, Dinge zu tun, Funktionalität".
Dimitrios Kalemis
Danke für die Klarstellung Dimitrios.
Jhegedus
3
"Monaden können weniger als Pfeile" - das ist absolut falsch. Monaden sind mächtiger als Pfeile, weshalb einige Dinge, die Pfeile sind, keine Monaden sind.
Matthias Berndt
3
Ich stimme Matthias hier zu. Jede Monade ist ein Funktor, aber nicht jeder Pfeil ist eine Monade. Sie müssen ArrowApplyeine Monade von einem Pfeil bekommen. Andererseits kann jede Monade überKleisli
WorldSEnder