Obwohl Monaden in Java implementiert werden können, ist jede Berechnung, die sie betrifft, dazu verdammt, eine chaotische Mischung aus Generika und geschweiften Klammern zu werden.
Ich würde sagen, dass Java definitiv nicht die Sprache ist, die verwendet wird, um ihre Arbeit zu veranschaulichen oder ihre Bedeutung und Essenz zu studieren. Zu diesem Zweck ist es weitaus besser, JavaScript zu verwenden oder einen zusätzlichen Preis zu zahlen und Haskell zu lernen.
Wie auch immer, ich signalisiere Ihnen, dass ich gerade eine Zustandsmonade mit den neuen Java 8-Lambdas implementiert habe . Es ist definitiv ein Haustierprojekt, aber es funktioniert in einem nicht trivialen Testfall.
Sie finden es vielleicht in meinem Blog , aber ich werde Ihnen hier einige Details geben.
Eine Zustandsmonade ist im Grunde eine Funktion von einem Zustand zu einem Paar (Zustand, Inhalt) . Normalerweise geben Sie dem Status einen generischen Typ S und dem Inhalt einen generischen Typ A.
Da Java keine Paare hat, müssen wir sie mit einer bestimmten Klasse modellieren. Nennen wir es Scp (State-Content-Paar), das in diesem Fall einen generischen Typ Scp<S,A>
und einen Konstruktor hat new Scp<S,A>(S state,A content)
. Danach können wir sagen, dass die monadische Funktion einen Typ haben wird
java.util.function.Function<S,Scp<S,A>>
das ist ein @FunctionalInterface
. Das heißt, dass die einzige Implementierungsmethode aufgerufen werden kann, ohne sie zu benennen, wobei ein Lambda-Ausdruck mit dem richtigen Typ übergeben wird.
Die Klasse StateMonad<S,A>
ist hauptsächlich ein Wrapper um die Funktion. Sein Konstruktor kann zB mit aufgerufen werden
new StateMonad<Integer, String>(n -> new Scp<Integer, String>(n + 1, "value"));
Die Zustandsmonade speichert die Funktion als Instanzvariable. Es ist dann notwendig, eine öffentliche Methode bereitzustellen, um darauf zuzugreifen und es dem Staat zuzuführen. Ich habe beschlossen, es zu nennen s2scp
("State-to-State-Content-Paar").
Um die Definition der Monade zu vervollständigen, müssen Sie eine Unit- Methode (auch bekannt als return ) und eine Bind- Methode (auch bekannt als flatMap ) angeben . Persönlich bevorzuge ich es, die Einheit als statisch anzugeben, während bind ein Instanzmitglied ist.
Im Fall der Staatsmonade muss die Einheit wie folgt sein:
public static <S, A> StateMonad<S, A> unit(A a) {
return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a));
}
while bind (als Instanzmitglied) ist:
public <B> StateMonad<S, B> bind(final Function<A, StateMonad<S, B>> famb) {
return new StateMonad<S, B>((S s) -> {
Scp<S, A> currentPair = this.s2scp(s);
return famb(currentPair.content).s2scp(currentPair.state);
});
}
Sie bemerken, dass bind einen generischen Typ B einführen muss, da dies der Mechanismus ist, der die Verkettung heterogener Zustandsmonaden ermöglicht und dieser und jeder anderen Monade die bemerkenswerte Fähigkeit verleiht, die Berechnung von Typ zu Typ zu verschieben.
Ich würde hier mit dem Java-Code aufhören. Das komplexe Zeug befindet sich im GitHub-Projekt. Im Vergleich zu früheren Java-Versionen entfernen Lambdas viele geschweifte Klammern, aber die Syntax ist immer noch ziemlich kompliziert.
Nebenbei zeige ich, wie ähnlicher Status-Monadencode in anderen Mainstream-Sprachen geschrieben werden kann. Im Falle der Scala, bind (die in diesem Fall muss aufgerufen werden flatMap ) liest sich wie
def flatMap[A, B](famb: A => State[S, B]) = new State[S, B]((s: S) => {
val (ss: S, aa: A) = this.s2scp(s)
famb(aa).s2scp(ss)
})
in der Erwägung, dass die Bindung in JavaScript mein Favorit ist; 100% funktional, schlank und gemein, aber natürlich typlos:
var bind = function(famb){
return state(function(s) {
var a = this(s);
return famb(a.value)(a.state);
});
};
<shameless> Ich schneide hier ein paar Ecken ab, aber wenn Sie an den Details interessiert sind, finden Sie sie auf meinem WP-Blog. </ shameless>