[JAVA] Monad est un modèle de conception pour "exprimer l'existence d'effets secondaires dans une méthode avec un type de retour"

J'ai écrit cet article pour expliquer aux programmeurs Java "à quoi sert Monad?"

Quels sont les effets secondaires de la méthode?

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)

Alors, comment dites-vous qu'il y a des effets secondaires dans la fonction?

  • Ecrire dans le document → Le document n'est pas écrit, il devient obsolète --Annoter → Oubliez de l'ajouter ou de l'ajouter lorsque vous n'en avez pas besoin

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" **

Si le type de retour peut exprimer l'existence d'effets secondaires ...

//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?

M. Monad "Utilisez le DSL en langue!"

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 ...

  • Cependant, sans utiliser le vocabulaire DSL en langue
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)

Implémentation Java de State Monad (version Java 8)

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());
    }

}

Scala est plus facile à écrire

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/

"Mais ... Monad est un langage fonctionnel, n'est-ce pas? Cela n'a pas d'importance pour moi ..."

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