Follow my new blog

Donnerstag, 31. Dezember 2009

Linksammlung zum Thema Monads [OOP 2010]

image Bei der Beschäftigung mit Funktionaler Programmierung stoße ich immer wieder auf den Begriff Monad. Leider bringt mich jedoch der zugehörige Wikipedia-Artikel bei dessen Verständnis nicht weiter. Deshalb habe ich jetzt ein wenig gegooglet. Für mich als Entwickler, der bisher (vor allem) mit imperativen Programmiersprachen zu tun hatte, sind dabei die folgenden Beiträge herausgekommen, die ich als verständnisfördernd empfinde.
So ganz bin ich mit diesen Beiträgen allerdings immer noch nicht zufrieden. Ich finde die Erklärungen für Monads immer noch recht theoretisch und fern der imperativen objektorientierten Programmierpraxis.

Wie würde ich nun Monads erklären? Hm... mal sehen. In einem zukünftigen Beitrag versuche ich das mal.

Kommentare:

HiQ-Software hat gesagt…

Hi Ralf,

anbei ein weiterer Link mit Beiträgen über Monads.

A Neighborhood of Infinity

Mir ist auch noch nicht 100% klar was es mit Monads auf sich hat. Einen Monad zeichnet u.a. aus, dass man bestimmte Operatoren (z.B. Bind) auf ihm definieren kann.

In meinem Verständnis sind Monads Patterns der funktionalen Programmierung.

Es gibt ja nicht nur DEN Monad sondern mehre Typen z.B. den Continuation Monad, Maybe Monad, State Monad etc.

Benjamin

Mike Bild hat gesagt…

So weit mir das klar ist (ich bin allerdings C#-Entwickler und kein Haskell oder F#-Entwickler) ist der Grundgedanke von Monaden, dass verhindern von Verunreinigungen einer reinen Funktionen (http://msdn.microsoft.com/de-de/library/bb669139.aspx). Diese zeichnen sich ja unter anderen durch ihre Freiheit von Neben- bzw. Seiteneffekten, wie bsw. IO, aus. Nun ist ein Programm ohne IO in der Praxis so ziemlich sinnlos und deshalb können eine ganze Reihe von Monaden (IO, State, usw.) als Kapselung der Seiteneffekte an die reine Funktion übergeben werden. Wie das in C# elegant bewerkstelligt werden kann (Schön wär's!), frag ich mich im Augenblick allerdings auch.

Ralf Westphal - One Man Think Tank hat gesagt…

@Mike: Mit deiner Beschreibung hast du natürlich Recht. Es bleibt die Frage: Warum der Aufstand, "Verunreinigungen" zu vermeiden?

Meine Antwort derzeit: Es geht um Composability. Ohne Monaden hab ich es schwerer, den Effekten einer "Computation" zu folgen. Ich sehe den Schritten nicht wirklich an, was sie für Effekte haben. Die sind nicht "zentralisiert", sondern können alles mögliche betreffen.

Mit einer Monade aber schaffe ich einen "Container" für Effekte jenseits der Berechnung einer Funktion (z.B. Ausgaben in Dateien, Fehlerinformationen).

Für die Dauer einer "Computation" ist die Monadeninstanz der Kontext. Er umschließt die "Computation" oder er spannt einen Raum für sie auf.

Deshalb spricht Brian Beckman auch immer mal wieder davon, dass ein OS Prozess oder auch eine CLR Instanz mit ihrem Hauptspeicher eine "ambient monad" sei.

In C# ist IEnumerable eine Monade, auf der Operationen mit LINQ ausgeführt werden. Insb SelectMany() ist dafür ein Ausdruck.

Ich grübel mal weiter an einem knackigeren Beispiel als dem, was normalerweise so gegeben wird. Identity oder Maybe Monaden find ich nicht so spannend.

-Ralf

Mike Bild hat gesagt…

@Ralf: Nun, mmmhh, aha ja OK. Ich bin konzeptionell immer davon ausgegangen, dass Monaden reine Funktionen über Argumente erweitern und damit die effektfreie Komposition von Operationen ermöglicht wird. (Wie gesagt ist mir der genaue praktische Einsatz von Monaden unklar.) Das es durch einen Raum, besser gesagt äußeren Kontext, geschehen kann ist mir nicht in den Sinn gekommen.

Kann das mal laut gedacht folgendes heißen?
Ein IO-Monad, bsw. ConsoleOut-Monad, stellt einen Raum/Kontext zur Verfügung. Jedes Ergebnis der Funktionen (evtl. Funktions-Pipelines) innerhalb des IO-Monad-Kontextes werden unweigerlich zu einer Konsolen-Ausgabe führen?

Und warum ist SelectMany() im Vergleich zu Select/Map so besonders?

Ralf Westphal - One Man Think Tank hat gesagt…

@Mike: So wie ich Monaden derzeit verstehe, spannen sie einen Raum auf (oder sagen wir Container), der sich auf eine "Computation" bezieht, also auf eine Schrittfolge von Anweisungen.

Statt, dass die Anweisungen globale Seiteneffekte haben (oder ist das ein Pleonasmus?), nehmen sie nur Veränderungen an der Monade vor, d.h. an dem ihnen zugewiesenen Container.

Dieser Container wird von Schritt zu Schritt quasi automatisch weitergereicht; das macht die Monade aus. Monaden sind also temporäre lokal-globale Sandkästen für Seiteneffekte.

Sie sind global für die Schritte einer "Computation". Aber sie sind lokal, weil sie eben nicht über sie hinaus existieren.

Beispiel:

var x = new[] {1, 2, 3, ...}.Select(...).Where(...).Select(...);

Die Monade ist das IEnumerable, das von Schritt zu Schritt "fließt". Es ist ein lokal, weil es nur hier existiert. Es ist aber global für alle Schritte.

Das es im Detail immer unterschiedliche IEnums sind und die keinen weiteren Zustand über irgendwelche Zahlen hinaus haben, ist nur ein Sonderfall.

-Ralf

Mike Bild hat gesagt…

Vielen Dank! Könnte man sagen, dass das einem durchreichen (fließen) eines Objektes (des Monaden), bzw. dessen konkrete Repräsentation, durch reine Funktionen (globaler Kontext des Monaden) entspricht und diese Funktionen den internen/lokalen Zustand/Eigenschaft(en) des Objektes oder dessen derzeitige Repräsentation bei jedem Schritt auf die nächste Operation ändern?

Nun, sehr interessant. Sehr schön finde ich die Idee, den Objektzustand vom Objektverhalten komplett getrennt zu sehen. Damit ergibt sich die Möglichkeit, dass korrekte Verhalten einer Funktion über eine Prüfung des Objektzustandes zu testen. Dennoch fallen mir, bis auf IQueryable und eigene Domänen-Modelle, keine weiteren "monadischen" C#-Standard-Typen für weitere Praxisbezüge ein.

Mike

Ralf Westphal - One Man Think Tank hat gesagt…

@Mike: Ja, ich denke, so ist das: Die Monade fließt durch die Schritte einer "Computation". Und diese Schritte verändern die Monade bzw. erzeugen immer neue Monaden anderen Typs.

Nun könnte man ja aber sagen: Dann machen wir einfach eine normale Klasse pro "Computation", geben der die Schritte der "Computation" als Methoden und rufen die Methoden nacheinander auf. Je "Computation" Aufruf wird eine Instanz der Klasse erzeugt, damit deren Zustand lokal-global ist.

Das würde gehen - ist aber nur die Variante für arme Leute ;-) Denn der fehlt die entscheidende Motivation der Funktionalen Programmierung: Composability.

Die Schritt-Methoden einer solchen Klasse ließen sich eben nicht in anderen Zusammenhängen wiederverwenden. Das ist aber der Trick der Monade! Sie bietet mit Bind() einen Mechanismus, der immer wieder neuen Funktionen erlaubt, "an ihr teilzunehmen". So wie bei LINQ: Da kann ich mit den Extension Methods beliebig viele Select() Aufrufe hintereinander schalten (die jeder für einen neuen Schritt stehen). An IEnum ändert das nichts.

-Ralf