web-dev-qa-db-fra.com

Monades avec Java 8

Pour aider à comprendre ce qu'est une monade, quelqu'un peut-il donner un exemple en utilisant Java? Sont-ils possibles?

Les expressions lambda sont possibles avec Java si vous téléchargez la pré-version compatible JDK8 compatible lambda à partir d’ici/ http://jdk8.Java.net/lambda/

Un exemple de lambda utilisant ce kit JDK est présenté ci-dessous. Quelqu'un peut-il fournir une monade comparable?

public interface TransformService {
        int[] transform(List<Integer> inputs);
    }
    public static void main(String ars[]) {
        TransformService transformService = (inputs) -> {
            int[] ints = new int[inputs.size()];
            int i = 0;
            for (Integer element : inputs) {
                ints[i] = element;
            }
            return ints;
        };

        List<Integer> inputs = new ArrayList<Integer>(5) {{
            add(10);
            add(10);
        }};
        int[] results = transformService.transform(inputs);
    }
74
NimChimpsky

Juste FYI:

La proposition JDK8 Facultatif classe satisfait à les trois lois de la monade . Voici un Gist démontrant que.

Une monade suffit à fournir deux fonctions conformes à la loi trois.

Les deux fonctions:

  1. Placez un valeur dans un contexte monadique} _

    • Haskell est peut-être: returnJust
    • Option de Scala: Some
    • Option de Java fonctionnel: Option.some
    • JDK8 en option: Optional.of
  2. Appliquer une fonction dans un contexte monadique

    • Haskell's Maybe: >>= (aka bind)
    • Option de Scala: flatMap
    • Option de Java fonctionnel: flatMap
    • JDK8 en option: flatMap

Veuillez consulter le ci-dessus Gist pour une démonstration en Java des trois lois. 

REMARQUE: L'un des éléments clés à comprendre est la signature de la fonction à appliquer dans un contexte monadique: elle prend le type de valeur brute et renvoie le type monadique. 

En d'autres termes, si vous avez une instance de Optional<Integer>, les fonctions que vous pouvez transmettre à sa méthode flatMap auront la signature (Integer) -> Optional<U>, où U est un type de valeur qui ne doit pas nécessairement être Integer, par exemple String:

Optional<Integer> maybeInteger = Optional.of(1);

// Function that takes Integer and returns Optional<Integer>
Optional<Integer> maybePlusOne = maybeInteger.flatMap(n -> Optional.of(n + 1));

// Function that takes Integer and returns Optional<String>
Optional<String> maybeString = maybePlusOne.flatMap(n -> Optional.of(n.toString));

Vous n'avez besoin d'aucune sorte d'interface Monad pour coder de cette façon ou pour penser de cette façon. En Scala, vous ne codez pas vers une interface Monad (sauf si vous utilisez la bibliothèque Scalaz ...). Il semble que JDK8 habilitera les utilisateurs Java à utiliser ce style de calculs monadiques chaînés.

J'espère que c'est utile!

_/Mise à jour: Blogué à ce sujet ici .

74
ms-tg

Java 8 aura des lambdas; les monades sont une toute autre histoire. Ils sont assez difficiles à expliquer en programmation fonctionnelle (comme en témoigne le grand nombre de tutoriels sur le sujet en haskell et en scala).

Les monades sont une caractéristique typique des langages fonctionnels typés statiquement. Pour les décrire en langage OO, vous pouvez imaginer une interface Monad. Les classes qui implémentent Monad seraient alors appelées «monadiques», à condition que, dans la mise en œuvre de Monad, la mise en œuvre obéisse à ce que l'on appelle les «lois de la monade». Le langage fournit alors un sucre syntaxique qui rend le travail avec les instances de la classe Monad intéressant.

Maintenant, Iterable en Java n'a rien à voir avec des monades, mais comme exemple d'un type que le compilateur Java traite spécialement (la syntaxe foreach fournie avec Java 5), ​​considérons ceci:

Iterable<Something> things = getThings(..);
for (Something s: things) {  /* do something with s */ }

Ainsi, alors que nous aurions pu utiliser les méthodes Iterable de Iterator (hasNext et company) dans une boucle for de style ancien, Java nous octroie ce sucre syntaxique comme un {cas spécial} _.

Donc, tout comme les classes qui implémentent Iterable et Iterator doivent obéir aux lois Iterator (Exemple: hasNext doit renvoyer false s'il n'y a pas d'élément suivant) pour être utile dans la syntaxe foreach - il existerait plusieurs monadiques classes _ qui seraient utile avec une notation do correspondante (comme on l'appelle dans Haskell) ou la notation for de Scala.

Alors -

  1. Quels sont les bons exemples de classes monadiques?
  2. À quoi ressemblerait le sucre syntaxique pour y faire face?

En Java 8, je ne sais pas - je connais la notation lambda, mais je ne connais pas d’autres sucres syntaxiques spéciaux, je vais donc devoir vous donner un exemple dans une autre langue.

Les monades servent souvent de classes conteneur (les listes en sont un exemple). Java a déjà Java.util.List qui n'est évidemment pas monadique, mais voici Scala:

val nums = List(1, 2, 3, 4)
val strs = List("hello", "hola")
val result = for { // Iterate both lists, return a resulting list that contains 
                   // pairs of (Int, String) s.t the string size is same as the num.
  n <- nums        
  s <- strs if n == s.length 
} yield (n, s)
// result will be List((4, "hola")) 
// A list of exactly one element, the pair (4, "hola")

Quel est le sucre (à peu près) syntaxique pour: 

val nums = List(1, 2, 3, 4)
val strs = List("hello", "hola")
val results = 
nums.flatMap( n =>                 
  strs.filter(s => s.size == n).   // same as the 'if'
       map(s => (n, s))            // Same as the 'yield'
)
// flatMap takes a lambda as an argument, as do filter and map
// 

Cela montre une fonctionnalité de Scala où les monades sont exploitées pour fournir compréhensions de liste.

Donc, une List dans Scala est une monade, car elle obéit aux lois de la monade de Scala, qui stipule que toutes les implémentations de la monade doivent avoir des méthodes conformes flatMap, map et filter (si vous vous intéressez aux lois, l'entrée de blog "Monades are Elephants" a le meilleure description que j'ai trouvée jusqu'à présent). Et, comme vous pouvez le constater, les lambdas (et HoF) sont absolument nécessaires mais pas suffisants pour rendre ce genre de chose utile de manière pratique.

Il y a aussi beaucoup de monades utiles en plus des conteneurs. Ils ont toutes sortes d'applications. Mon favori doit être la monade Option de Scala (la monade Maybe de Haskell), qui est un type d'encapsuleur qui génère sécurité null: la page de l'API Scala pour la monade Option a un exemple très simple d'utilisation: http://www.scala-lang.org/api/current/scala/Option.html En Haskell, les monades sont utiles pour représenter l’OI, en tant que moyen de contourner le fait que le non-monadique Haskell le code a un ordre d'exécution indéterminé.

Avoir lambdas est un premier petit pas dans le monde de la programmation fonctionnelle; monads requiert à la fois la convention monad et un assez grand nombre de types monadiques utilisables, ainsi que sucre syntactique pour rendre leur utilisation agréable et utile.

Scala étant sans doute le langage le plus proche de Java qui autorise également la programmation fonctionnelle (monadique), consultez ce didacticiel Monad pour Scala si vous êtes (toujours) intéressé: http://james-iry.blogspot.jp /2007/09/monads-are-elephants-part-1.html

Un rapide repérage sur Google montre qu'il existe au moins une tentative en Java: https://github.com/RichardWarburton/Monads-in-Java -

Malheureusement, expliquer les monades en Java (même avec les lambdas) est aussi difficile que d’expliquer la programmation orientée objet à part entière en ANSI C (au lieu de C++ ou Java).

55
Faiz

Voici ce qui est difficile à comprendre à propos des monades: les monades sont un motif, pas un type spécifique. Les monades sont une forme, elles sont abstraites interface (pas au sens de Java) plus qu’il s’agit d’une donnée concrète structure. En conséquence, tout tutoriel basé sur des exemples est condamné à incomplétude et échec . [...] La seule façon de comprendre les monades est de les voir telles qu'elles sont: une construction mathématique.

Les monades ne sont pas des métaphores par Daniel Spiewak


Monades dans Java SE 8

Liste monade

interface Person {
    List<Person> parents();

    default List<Person> greatGrandParents1() {
        List<Person> list = new ArrayList<>();
        for (Person p : parents()) {
            for (Person gp : p.parents()) {
                for (Person ggp : p.parents()) {

                    list.add(ggp);
                }
            }
        }
        return list;
    }

    // <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
    default List<Person> greatGrandParents2() {
        return Stream.of(parents())
                .flatMap(p -> Stream.of(p.parents()))
                .flatMap(gp -> Stream.of(gp.parents()))
                .collect(toList());
    }
}

Peut-être monade

interface Person {
    String firstName();
    String middleName();
    String lastName();

    default String fullName1() {
        String fName = firstName();
        if (fName != null) {
            String mName = middleName();
            if (mName != null) {
                String lName = lastName();
                if (lName != null) {
                    return fName + " " + mName + " " + lName;
                }
            }
        }
        return null;
    }

    // <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
    default Optional<String> fullName2() {
        return Optional.ofNullable(firstName())
                .flatMap(fName -> Optional.ofNullable(middleName())
                .flatMap(mName -> Optional.ofNullable(lastName())
                .flatMap(lName -> Optional.of(fName + " " + mName + " " + lName))));
    }
}

Monad est un modèle générique pour imbriqué encapsulation de flux de contrôle . I.e. un moyen de créer des composants réutilisables à partir d'idiomes impératifs imbriqués .

Il est important de comprendre qu’une monade n’est pas simplement une classe d’emballage générique avec une opération flat map. Par exemple, ArrayList avec une méthode flatMap ne sera pas une monade .. Parce que lois de la monade interdisent les effets secondaires.

Monad est un formalisme. Il décrit la structure, indépendamment du contenu ou de la signification. Les gens ont du mal à se rapporter à des choses (abstraites) dépourvues de sens .. Ils inventent donc des métaphores qui ne sont pas des monades.

_ {Voir aussi:conversation entre Erik Meijer et Gilad Bracha. 

4
user2418306

la seule façon de comprendre les monades consiste à écrire un ensemble de bibliothèques de combinateurs, à remarquer la duplication résultante, puis à découvrir par vous-même que les monades vous permettent d'éliminer cette duplication. En découvrant cela, tout le monde construit une intuition sur ce qu'est une monade… mais cette intuition n'est pas le genre de chose que vous pouvez communiquer directement à quelqu'un d'autre - il semble que tout le monde doive passer par la même expérience de généralisation à des monades à partir de certains exemples de bibliothèques de combinateurs. toutefois

ici, j'ai trouvé du matériel pour apprendre Mondas.

espérons être utile pour vous aussi.

codecommit

james-iry.blogspot

debasishg.blogspot

4
Morteza Adi

Cet article de blog donne un exemple pas à pas de la manière dont vous pourriez implémenter un type Monad (interface) en Java, puis l'utiliser pour définir le monad Maybe, en tant qu'application pratique.

Cet article explique qu’il existe une monade intégrée au langage Java, soulignant le fait que les monades sont plus courantes que beaucoup de programmeurs et que les codeurs les réinventent souvent par inadvertance .

2
Katie J. Ots

J'aime penser aux monades de façon un peu plus mathématique (mais toujours informelle). Après cela, je vous expliquerai la relation avec l’une des monades de Java 8 CompletableFuture .

Tout d’abord, une monade M est un foncteur . En d'autres termes, il transforme un type en un autre type: Si X est un type (par exemple, String), nous avons un autre type, M<X> (par exemple, List<String>). De plus, si nous avons une transformation/fonction X -> Y de types, nous devrions obtenir une fonction M<X> -> M<Y>.

Mais il y a plus de données sur une telle monade. Nous avons une unité appelée fonction X -> M<X> pour chaque type X. En d'autres termes, chaque objet de X peut être enveloppé de manière naturelle dans la monade.

La donnée la plus caractéristique d’une monade, cependant, est son produit: une fonction M<M<X>> -> M<X> pour chaque type X.

Toutes ces données devraient satisfaire à certains axiomes tels que la fonction de mémoire, l’associativité, les lois des unités, mais je n’entrerai pas dans les détails ici et cela n’a pas d’importance non plus dans la pratique.

Nous pouvons maintenant déduire une autre opération pour les monades, qui est souvent utilisée comme définition équivalente pour les monades, l'opération de liaison: Une valeur/un objet dans M<X> peut être lié à une fonction X -> M<Y> pour générer une autre valeur dans M<Y>. Comment y parvenons-nous? Eh bien, d’abord, nous appliquons la fonctorialité à la fonction pour obtenir une fonction M<X> -> M<M<Y>>. Ensuite, nous appliquons le produit monadique à la cible pour obtenir une fonction M<X> -> M<Y>. Nous pouvons maintenant brancher la valeur de M<X> pour obtenir une valeur dans M<Y> comme vous le souhaitez. Cette opération de liaison est utilisée pour chaîner plusieurs opérations monadiques ensemble.

Venons maintenant au CompletableFuture example, c.-à-d. CompletableFuture = M. Imaginez un objet de CompletableFuture<MyData> comme un calcul effectué de manière asynchrone et qui génère un objet de MyData comme résultat dans le futur. Quelles sont les opérations monadiques ici?

  • la fonctorialité est réalisée avec la méthode thenApply: le calcul est d'abord effectué et dès que le résultat est disponible, la fonction attribuée à thenApply est appliquée pour transformer le résultat en un autre type
  • l'unité monadique est réalisée avec la méthode completedFuture: comme le dit la documentation, le calcul résultant est déjà terminé et donne immédiatement la valeur donnée
  • le produit monadique n'est pas réalisé par une fonction, mais l'opération de liaison ci-dessous lui est équivalente (avec sa fonction de mémoire) et sa signification sémantique est simplement la suivante: pour un calcul de type CompletableFuture<CompletableFuture<MyData>> ce calcul asynchrone donne un autre calcul en CompletableFuture<MyData> qui à son tour donne plus tard une certaine valeur dans MyData, donc effectuer les deux calculs après l'autre donne un calcul au total
  • l'opération de liaison résultante est réalisée par la méthode thenCompose

Comme vous le voyez, les calculs peuvent maintenant être regroupés dans un contexte particulier, à savoir asynchronicity . Les structures monadiques générales nous permettent d’enchaîner de tels calculs dans le contexte donné. CompletableFuture est par exemple utilisé dans Lagom framework pour construire facilement des gestionnaires de requêtes hautement asynchrones, sauvegardés de manière transparente par des pools de threads efficaces (au lieu de gérer chaque requête par un thread dédié).

0
Werner Thumann

En dépit de toute controverse sur le fait que Optional satisfasse ou non les lois de la Monad, j'aime bien regarder Stream, Optional et CompletableFuture de la même manière. En vérité, ils fournissent tous une flatMap() et c’est tout ce qui m’intéresse et permettez-moi d’embrasser le " la composition de bon goût des effets secondaires " (cité par Erik Meijer). Donc, nous pouvons avoir les Stream, Optional et CompletableFuture correspondants de la manière suivante:

  

En ce qui concerne les monades, je simplifie généralement en pensant uniquement à flatMap() (extrait de " Principes de la programmation réactive " bien sûr d'Erik Meijer):

Eric-Meijer-flatMap

0
Miguel Gamboa