Ich habe diesen Artikel geschrieben, um Java-Programmierern zu erklären, "wofür ist Monad nützlich?"
Oder so ähnlich
public int getAge() {
age += 1; //Hör auf!
return age;
}
Oder so ähnlich
public double calculateTax(double price) {
System.out.println("raw price:" + price); //Eh, zeigen Sie auch ...?
return price * 0.08;
}
Oder so ähnlich
public boolean isLegalPerson(Person person) {
if (person.getType().equals(PersonType.UNKNOWN))
throw new RuntimeException(); //Zumindest eine Inspektionsausnahme machen ...
return person.getType().equals(PersonType.LEGAL);
}
Alle Aktionen außer der Eingabe → Ausgabe der Methode (Funktion ≒) sind Nebenwirkungen. Natürlich sind nicht alle Nebenwirkungen schlecht. Aber unerwartete Nebenwirkungen sind schlimm! !! !! </ font> (Beispiel wie oben)
Dann wäre es schön, wenn wir ausdrücken könnten, dass die Funktion Nebenwirkungen durch ** Rückgabetyp ** hat. ** → Das Entwurfsmuster dafür ist "Monade" **
//Sie können sehen, dass der Wert neu geschrieben wird, indem Sie sich nur den Typ ansehen!
public State<Integer> getAge() {
...
}
//Sie können sehen, dass es angezeigt werden kann, indem Sie nur auf die Form schauen!
public IO<Double> calculateTax(double price) {
....
}
//Sie können sehen, dass Sie eine Ausnahme auslösen können, indem Sie sich nur den Typ ansehen!
public Try<Boolean> isLegalPerson(Person person) {
....
}
Wie realisieren Sie diesen Mechanismus?
Sie können ein DSL in der Sprache erstellen, um anzuzeigen, dass das Monadenmuster Nebenwirkungen aufweist. Mit anderen Worten, Monad ist ein Entwurfsmuster zum Erstellen einer Minisprache (DSL), die darauf spezialisiert ist, "das Vorhandensein von Nebenwirkungen mit einem Rückgabetyp anzuzeigen". [^ 1] [^ 1]: Wenn Sie Angst haben, über das Kreisuri-Gebiet zu sprechen, gehen Sie bitte irgendwohin ...
System.out.println("Kennst du Monad!");
throw new RuntimeException("Kennst du Monad!");
Wenn Sie es normal schreiben, macht es keinen Sinn, das Monadenmuster zu verwenden. Daher ist es erforderlich, innerhalb dieses Frames mit einem in der Sprache erstellten DSL zu programmieren, das mit dem Monadenmuster erstellt wurde. Beachten Sie, dass Sie sonst nicht in der Lage sind, "das Vorhandensein von Nebenwirkungen anzugeben".
(Berühren Sie die Servlet-API nicht direkt, obwohl Sie Spring MVC verwenden. Es ist nicht sinnvoll, Spring MVC zu verwenden.)
Als Beispiel erstellen wir ein ** "DSL in der Sprache, das den Status in einer Funktion mit einem Rückgabetyp" ** unter Verwendung eines Monadenmusters in Java ausdrücken kann.
package monad;
import java.util.function.Function;
class Pair<A> {
public final Integer state;
public final A value;
Pair(A a, Integer s) {
this.value = a;
this.state = s;
}
}
public class State<A> {
public Function<Integer, Pair<A>> func;
public State(Function<Integer, Pair<A>> func) { this.func = func; }
public static <A> State<A> unit(A a) {
return new State<>(s -> new Pair<>(a, s));
}
public <B> State<B> bind(Function<A, State<B>> nextFunc) {
return new State<>(s -> {
Pair<A> pair = func.apply(s);
return nextFunc.apply(pair.value).func.apply(pair.state);
});
}
public static State<Integer> get() {
return new State<>(s -> new Pair<>(s, s));
}
public static State<Void> put(final Integer s) {
return new State<>(__ -> new Pair<>(null, s));
}
}
Verwendung von State Monad für Java
import static monad.State.get;
import static monad.State.put;
import static monad.State.unit;
public class MonadicExample {
//Das Vorhandensein von Nebenwirkungen wird im Rückgabetyp ausgedrückt
public static State<Integer> getAge() {
//Dieses DSL in der Sprache verwendet die Bindemethode anstelle des Zuweisungsoperators
return get().bind(age ->
put(age + 1).bind(__ ->
get()));
}
public static void main(String[] args) {
//Ich rufe getAge zweimal auf, daher wird 2 angezeigt
getAge().bind(__ ->
getAge().bind(age -> {
System.out.println(age);
return unit(null);
})).func.apply(0);
}
}
Java-Monaden sind ein bisschen (ziemlich?) Schwer zu erkennen ... Java-Grenzen
Wenn Sie denselben Prozess ohne Verwendung der Statusmonade schreiben, sieht er folgendermaßen aus
public class NonMonadicExample {
private static int age = 0;
public static int getAge() {
age = age + 1; //Ich bemerke die Existenz dieser Nebenwirkung nicht! !!
return age;
}
public static void main(String[] args) {
//Ich rufe getAge zweimal auf, daher wird 2 angezeigt
getAge();
System.out.println(getAge());
}
}
In funktionalen Sprachen gibt es einen speziellen Syntaxzucker, der das Schreiben von DSLs in Sprachen erleichtert, die mit monadischen Mustern erstellt wurden. Mit Scala können Sie so mit Syntaxzucker schreiben, der Monad DSL gewidmet ist.
def getAge(): State[Int] = for {
age <- get()
_ <- put(age + 1)
age <- get()
} yield age
(for {
_ <- getAge()
age <- getAge()
} yield println(age)).run(0)
Wenn Sie mehr über andere Monadenmuster als staatliche Monaden erfahren möchten, lesen Sie Bücher wie "Scala Functional Design & Programming"! https://www.amazon.co.jp/dp/B00WM54V5Q/
Das kann ich nicht sagen ~ Optionale Typen, CompletableFuture-Typen, von Java8 hinzugefügte StreamAPI und Observable of RxJava (Bibliothek für reaktive Programmierung), die in letzter Zeit beliebt ist, sind tatsächlich im Monadenmuster implementiert. Es ist schwer zu schreiben, da es keine dedizierte Syntax wie Scala gibt.
Außerdem scheint es die folgende Bibliothek als Mechanismus zu geben, um das Schreiben von Monaden in Java zu vereinfachen. https://github.com/soabase/soabase-halva/blob/master/halva/src/main/java/io/soabase/halva/comprehension/README.md http://fits.hatenablog.com/entry/2015/05/16/204101
Recommended Posts