Ich werde eine sprachunabhängige Beschreibung von Monaden wie diese verwenden und zuerst Monoide beschreiben:
Ein Monoid ist (ungefähr) eine Menge von Funktionen, die einen bestimmten Typ als Parameter annehmen und denselben Typ zurückgeben.
Eine Monade ist (ungefähr) eine Reihe von Funktionen, die einen Wrapper- Typ als Parameter verwenden und denselben Wrapper-Typ zurückgeben.
Beachten Sie, dass dies Beschreibungen und keine Definitionen sind. Fühlen Sie sich frei, diese Beschreibung anzugreifen!
In einer OO-Sprache erlaubt eine Monade Operationskompositionen wie:
Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
Beachten Sie, dass die Monade die Semantik dieser Operationen und nicht die enthaltene Klasse definiert und steuert.
Traditionell verwenden wir in einer OO-Sprache eine Klassenhierarchie und -vererbung, um diese Semantik bereitzustellen. So hätten wir eine haben Bird
Klasse mit Methoden takeOff()
, flyAround()
und land()
, und Ente würden diejenigen erben.
Aber dann bekommen wir Ärger mit flugunfähigen Vögeln, weil es penguin.takeOff()
versagt. Wir müssen auf das Ausnahmewerfen und -handling zurückgreifen.
Bird
Wenn wir einmal sagen, dass Pinguin ein Pinguin ist , haben wir Probleme mit der Mehrfachvererbung, zum Beispiel wenn wir auch eine Hierarchie von Pinguinen haben Swimmer
.
Im Wesentlichen versuchen wir, Klassen in Kategorien einzuteilen (mit Entschuldigung an die Kategorietheoretiker) und die Semantik eher nach Kategorien als nach einzelnen Klassen zu definieren. Aber Monaden scheinen ein viel klarerer Mechanismus dafür zu sein als Hierarchien.
In diesem Fall hätten wir also eine Flier<T>
Monade wie das obige Beispiel:
Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
... und wir würden niemals a instanziieren Flier<Penguin>
. Wir könnten sogar statische Typisierung verwenden, um dies zu verhindern, möglicherweise mit einer Markierungsschnittstelle. Oder Laufzeitfähigkeitsprüfung zur Rettung. Aber wirklich, ein Programmierer sollte niemals einen Pinguin in Flier stecken, in dem gleichen Sinne, in dem sie niemals durch Null teilen sollten.
Es ist auch allgemeiner anwendbar. Ein Flieger muss kein Vogel sein. Zum Beispiel Flier<Pterodactyl>
oder Flier<Squirrel>
, ohne die Semantik dieser einzelnen Typen zu ändern.
Sobald wir die Semantik durch zusammensetzbare Funktionen in einem Container klassifizieren - anstelle von Typhierarchien - werden die alten Probleme mit Klassen behoben, die "irgendwie tun, irgendwie nicht" in eine bestimmte Hierarchie passen. Es erlaubt auch leicht und klar mehrere Semantiken für eine Klasse, wie Flier<Duck>
auch Swimmer<Duck>
. Es scheint, als hätten wir mit einer Impedanzinkongruenz zu kämpfen, indem wir das Verhalten anhand von Klassenhierarchien klassifizierten. Monaden gehen elegant damit um.
Meine Frage ist also, in der gleichen Weise, wie wir die Komposition der Vererbung vorgezogen haben, ist es auch sinnvoll, Monaden der Vererbung vorzuziehen?
(Übrigens war ich mir nicht sicher, ob dies hier oder in Comp Sci sein sollte, aber dies scheint eher ein praktisches Modellierungsproblem zu sein. Aber vielleicht ist es dort besser.)