J'ai écrit cet article pour expliquer aux programmeurs Java "à quoi sert Monad?"
Ou quelque chose comme ça
public int getAge() {
age += 1; //Arrête ça!
return age;
}
Ou quelque chose comme ça
public double calculateTax(double price) {
System.out.println("raw price:" + price); //Eh, affichez-vous aussi ...?
return price * 0.08;
}
Ou quelque chose comme ça
public boolean isLegalPerson(Person person) {
if (person.getType().equals(PersonType.UNKNOWN))
throw new RuntimeException(); //Faites au moins une exception d'inspection ...
return person.getType().equals(PersonType.LEGAL);
}
Toutes les actions autres que l'entrée → la sortie de la méthode (fonction ≒) sont des effets secondaires. Bien sûr, tous les effets secondaires ne sont pas mauvais. Mais les effets secondaires inattendus sont mauvais! !! !! </ font> (Exemple comme ci-dessus)
Ensuite, ce serait bien si nous pouvions exprimer que la fonction a des effets secondaires par ** type de retour **. ** → Le modèle de conception pour cela est "Monad" **
//Vous pouvez voir que la valeur est réécrite simplement en regardant le type!
public State<Integer> getAge() {
...
}
//Vous pouvez voir qu'il peut être affiché simplement en regardant le moule!
public IO<Double> calculateTax(double price) {
....
}
//Vous pouvez voir que vous pouvez lever une exception simplement en regardant le type!
public Try<Boolean> isLegalPerson(Person person) {
....
}
Alors, comment réalisez-vous ce mécanisme?
Vous pouvez créer un DSL en langue pour indiquer qu'il existe des effets secondaires dans le modèle monade. En d'autres termes, Monad est un modèle de conception pour créer un mini-langage (DSL) spécialisé dans le but de "signaler l'existence d'effets secondaires avec un type de retour". [^ 1] [^ 1]: Si vous avez peur de parler de la région de Kreisuri, allez quelque part ...
System.out.println("Connaissez-vous Monad!");
throw new RuntimeException("Connaissez-vous Monad!");
Si vous l'écrivez normalement, il ne sert à rien d'utiliser le motif monade. Par conséquent, il est nécessaire de programmer dans cette trame à l'aide d'un DSL en langage créé avec le modèle monade. Notez que dans le cas contraire, vous ne pourrez pas «déclarer l'existence d'effets secondaires».
(Ne touchez pas directement l'API Servlet même si vous utilisez Spring MVC, cela n'a pas de sens d'utiliser Spring MVC)
A titre d'exemple, créons ** "un DSL en langage qui peut exprimer l'état dans une fonction avec un type de retour" ** en utilisant un modèle monade en Java.
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));
}
}
Comment utiliser State Monad pour Java
import static monad.State.get;
import static monad.State.put;
import static monad.State.unit;
public class MonadicExample {
//La présence d'effets secondaires est exprimée dans le type de retour
public static State<Integer> getAge() {
//Ce DSL en langue utilise la méthode de liaison au lieu de l'opérateur d'affectation
return get().bind(age ->
put(age + 1).bind(__ ->
get()));
}
public static void main(String[] args) {
//J'appelle getAge deux fois, donc 2 s'affiche
getAge().bind(__ ->
getAge().bind(age -> {
System.out.println(age);
return unit(null);
})).func.apply(0);
}
}
Les monades Java sont un peu (assez?) Difficile à voir ... Limites Java
Si vous écrivez le même processus sans utiliser la monade d'État, cela ressemblera à ceci
public class NonMonadicExample {
private static int age = 0;
public static int getAge() {
age = age + 1; //Je ne remarque pas l'existence de cet effet secondaire! !!
return age;
}
public static void main(String[] args) {
//J'appelle getAge deux fois, donc 2 s'affiche
getAge();
System.out.println(getAge());
}
}
Dans les langages fonctionnels, il existe un sucre de syntaxe dédié pour faciliter l'écriture de DSL en langage créés avec des modèles monadiques. Avec scala, vous pouvez écrire comme ça avec le sucre de syntaxe dédié à Monad DSL.
def getAge(): State[Int] = for {
age <- get()
_ <- put(age + 1)
age <- get()
} yield age
(for {
_ <- getAge()
age <- getAge()
} yield println(age)).run(0)
Si vous voulez en savoir plus sur les modèles de monades autres que les monades d'État, lisez des livres tels que "Scala Functional Design & Programming"! https://www.amazon.co.jp/dp/B00WM54V5Q/
Je ne peux pas dire ça ~ Les types facultatifs, les types CompletableFuture, StreamAPI ajouté à partir de Java8 et Observable of RxJava (bibliothèque pour la programmation réactive), qui est populaire récemment, sont en fait implémentés dans un modèle monade. C'est difficile à écrire car il n'y a pas de syntaxe dédiée comme scala.
En outre, il semble y avoir la bibliothèque suivante comme mécanisme pour faciliter l'écriture de monades en Java. 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