Monades avec Java 8
Dans le but d'aider à comprendre ce qu'est une monade, quelqu'un peut-il fournir un exemple en utilisant java ? Sont-ils possibles ?
Les expressions Lambda sont possibles en utilisant java si vous téléchargez le JDK8 compatible LAMBDA pré-version à partir d'ici http://jdk8.java.net/lambda/
Un exemple d'un lambda utilisant ce JDK est montré ci-dessous, quelqu'un peut-il fournir une monade relativement simple ?
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);
}
7 réponses
Juste pour INFO:
La classe JDK8 optionnelle proposée satisfait les trois lois de la Monade . Voici un essentiel démontrant cela.
Tout ce qu'il faut être une Monade est de fournir deux fonctions qui sont conformes à trois lois.
, Les deux fonctions:
-
Place une valeur dans un contexte monadique
- Haskell est peut-être:
return
/Just
- Option de Scala:
Some
- Option fonctionnelle de Java:
Option.some
- JDK8 est facultatif:
Optional.of
- Haskell est peut-être:
-
Appliquer une fonction dans un contexte monadique
- Haskell peut-être:
>>=
(aliasbind
) - Option de Scala:
flatMap
- Option fonctionnelle de Java:
flatMap
- JDK8 est facultatif:
flatMap
- Haskell peut-être:
Veuillez consulter leci-dessus gist pour une démonstration java des trois lois.
NOTE: L'une des choses clés comprendre est la signature de la fonction à appliquer dans le 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 passer à sa méthode flatMap
auront la signature (Integer) -> Optional<U>
, où U
est un type de valeur qui ne doit pas ê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 de monade pour coder de cette façon, ou pour penser de cette façon. À Scala, vous ne le faites pas code à une Interface Monad (sauf si vous utilisez la bibliothèque Scalaz...). Il semble que JDK8 permettra aux gens de Java d'utiliser ce style de calculs monadiques chaînés aussi.
J'espère que cela est utile!
Mise à Jour: Blogué à propos de cette ici.
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 dans Haskell et Scala).
Les monades sont une caractéristique typique des langages fonctionnels statiquement typés. Pour les décrire en oo-speak, vous pouvez imaginer une interface Monad
. Les Classes qui implémentent Monad
seraient alors appelées 'monadiques', à condition qu'en implémentant Monad
l'implémentation obéisse à ce qui est connue comme la "monade lois". Le langage fournit alors du sucre syntaxique qui rend le travail avec des instances de la classe Monad
intéressant.
Maintenant {[7] } en Java n'a rien à voir avec les monades, mais à titre d'exemple d'un type que le compilateur Java traite spécialement (la syntaxe foreach
fournie avec Java 5), considérez ceci:
Iterable<Something> things = getThings(..);
for (Something s: things) { /* do something with s */ }
Alors que nous aurions pu utiliser les méthodes Iterator
de Iterable
(hasNext
et company) dans une boucle for
à l'ancienne, Java nous accorde ce sucre syntaxique comme un cas particulier.
De même que les classes qui implémentent Iterable
et Iterator
doivent obéir aux lois Iterator
(Exemple: hasNext
doit retourner false
s'il n'y a pas d'élément suivant) pour être utiles dans la syntaxe foreach
- il existerait plusieurs classes monadiques qui seraient utiles avec une notation do
correspondante (comme on l'appelle dans Haskell) ou la notation for
de Scala.
Donc -
- Quels sont les bons exemples de classes monadiques?
- Ce qui serait syntaxique sucre pour traiter avec eux ressembler?
En Java 8, Je ne sais pas - je suis au courant de la notation lambda mais je ne suis pas au courant d'autres sucres syntaxiques spéciaux, donc je vais devoir vous donner un exemple dans une autre langue.
Les monadesServent souvent de classes container (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")
Qui est (à peu près) le sucre 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 caractéristique de Scala où les monades sont exploitées pour fournir liste compréhensions .
Donc, un List
dans Scala est une monade, car elle obéit aux lois de la monade de Scala, qui stipulent que toutes les implémentations de la monade doivent être conformes flatMap
, map
et filter
méthodes (si vous êtes intéressé par les lois, l'entrée de blog "les monades sont des Éléphants" a la meilleure description que j'ai trouvée jusqu'à présent). Et, comme vous pouvez le voir, lambdas (et HoF) sont absolument nécessaires mais pas suffisants pour faire ce genre de chose utile en pratique.
Il y a un tas de monades utiles en plus des monades container-ish aussi bien. Ils ont toutes sortes d'applications. Mon préféré doit être la monade Option
dans Scala (la monade Maybe
dans Haskell), qui est un type de wrapper qui apporte null safety : la page de L'API Scala pour la monade Option
a un exemple d'utilisation très simple: http://www.scala-lang.org/api/current/scala/Option.html
Dans Haskell, les monades sont utiles dans représentant IO, comme un moyen de contourner le fait que le code Haskell non monadique a un ordre d'exécution indéterminé.
Avoir des lambdas est un premier petit pas dans le monde de la programmation fonctionnelle; monades exiger à la fois la convention monade et un ensemble assez grand de types monadiques utilisables, ainsi que sucre syntaxique pour rendre le travail avec eux amusant et utile.
Puisque Scala est sans doute le langage le plus proche de Java qui permet également la programmation fonctionnelle (monadique) , ne regardez ce tutoriel Monad pour Scala si vous êtes (encore) intéressé: http://james-iry.blogspot.jp/2007/09/monads-are-elephants-part-1.html
Un googling superficiel montre qu'il y a au moins une tentative de le faire en Java: https://github.com/RichardWarburton/Monads-in-Java -
Malheureusement, expliquer les monades en Java (même avec lambdas) est aussi difficile que d'expliquer la programmation orientée objet en ANSI C (au lieu de C++ ou Java).
Même si les monades peuvent être implémentées en Java, tout calcul les impliquant est voué à devenir un mélange désordonné de génériques et d'accolades.
Je dirais que Java est certainement Pas le langage à utiliser pour illustrer leur travail ou pour étudier leur signification et leur essence. Pour ce faire, il est préférable D'utiliser JavaScript ou de payer un prix supplémentaire et d'apprendre Haskell.
Quoi qu'il en soit, je vous signale que je viens d'implémenter une monade d'État en utilisant le nouveau Java 8 lambdas. C'est certainement un projet pour animaux de compagnie, mais cela fonctionne sur un cas de test non trivial.
Vous pouvez le trouver présenté sur mon blog , mais je vais vous donner quelques détails ici.
Une monade d'état est essentiellement une fonction d'un État à une paire (state,content) . Vous donnez généralement à l'état un type générique s et au contenu un type générique A.
Comme Java N'a pas de paires, nous devons les modéliser en utilisant une classe spécifique, appelons-la Scp (paire state-content), qui dans ce cas aura le type générique Scp<S,A>
et un constructeur new Scp<S,A>(S state,A content)
. Après cela, nous pouvons dire que la fonction monadique de type
java.util.function.Function<S,Scp<S,A>>
, Qui est un @FunctionalInterface
. C'est-à-dire que sa seule et unique méthode d'implémentation peut être invoquée sans la nommer, en passant une expression lambda avec le bon type.
La classe StateMonad<S,A>
est principalement un wrapper autour de la fonction. Son constructeur peut être invoqué par exemple avec
new StateMonad<Integer, String>(n -> new Scp<Integer, String>(n + 1, "value"));
Les magasins de la monade d'état la fonction en tant que variable d'instance. Il est alors nécessaire de fournir une méthode publique pour y accéder et lui alimenter l'état. J'ai décidé de l'appeler s2scp
("paire d'État à état-contenu").
Pour terminer la définition de la monade, vous devez fournir un unité de (aka return) et bind (aka flatMap) méthode. Personnellement, je préfère spécifier unit comme statique, alors que bind est un membre d'instance.
Dans le cas de la monade d'état, l'unité doit être ce qui suit:
public static <S, A> StateMonad<S, A> unit(A a) {
return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a));
}
Alors que bind (en tant que membre de l'instance) est:
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);
});
}
Vous remarquez que bind doit introduire un type B générique, car c'est le mécanisme qui permet le chaînage de monades d'États hétérogènes et donne à cette monade et à toute autre la capacité remarquable de déplacer le calcul de type en type.
Je m'arrêterais ici avec le code Java. Les choses complexes sont dans le projet GitHub. Par rapport aux versions Java précédentes, lambdas supprimer beaucoup d'accolades, mais la syntaxe est encore assez alambiquée.
Juste comme un aparté, je montre comment un code de monade d'état similaire peut être écrit dans d'autres langues grand public. Dans le cas de la Scala, bind (qui dans ce cas doit être appelé flatMap) se lit comme
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)
})
Alors que le bind en JavaScript est mon préféré; 100% fonctionnel, maigre et moyen mais-bien sûr-sans Type:
var bind = function(famb){
return state(function(s) {
var a = this(s);
return famb(a.value)(a.state);
});
};
Je coupe quelques coins ici, mais si vous êtes intéressé par les détails vous les trouverez sur mon blog WP.vergogne>
La seule façon de comprendre les monades est d'écrire un tas de bibliothèques de combinateurs, de remarquer la duplication qui en résulte, puis de découvrir par vous-même que les monades vous permettent de factoriser cette duplication. En découvrant cela, tout le monde construit une intuition pour 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 doit passer par la même expérience de généralisation aux monades à partir de quelques exemples concrets de combinator bibliothèque. cependant
Ici, j'ai trouvé des matériaux pour apprendre Mondas.
Espère être utile pour vous aussi.
Voici la chose à propos des monades qui est difficile à saisir: les monades sont un Motif, pas un type spécifique. Les monades sont une forme, elles sont un résumé interface (pas dans le sens Java) plus qu'ils ne sont des données concrètes structure. Par conséquent, tout tutoriel axé sur les exemples est voué à incomplétude et échec. [...] La seule façon de comprendre les monades est de les voir pour ce qu'elles sont: une construction mathématique.
Les Monades ne sont pas des métaphores, par Daniel Spiewak
Les Monades en 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 l'encapsulation de flux de contrôleimbriquée. C'est-à-dire un moyen de créer des composants réutilisables à partir d'idiomes impératifs imbriqués.
Important de comprendre qu'une monade n'est pas seulement une classe wrapper générique avec un planisphère opération.
Par exemple, ArrayList
avec une méthode flatMap
ne sera pas une monade.
Parce les lois de la monade interdisent les effets secondaires.
Monade 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) sans signification. Donc, ils viennent avec des métaphores qui ne sont pas des monades.
Voir aussi: conversation entre Erik Meijer et Gilad Bracha.
Cet article de blog donne un exemple étape par étape de la façon dont vous pouvez implémenter un type de monade (interface) en Java, puis l'utiliser pour définir la Monade Maybe, comme une application pratique.
Ce post explique qu'il existe une monade intégrée dans le langage Java, soulignant le fait que les monades sont plus courantes que de nombreux programmeurs ne le pensent et que les codeurs les réinventent souvent par inadvertance .
J'aime penser à des monades de façon légèrement plus mathématique (mais toujours informelle). Après cela, je vais expliquer la relation avec L'une des monades de Java 8 CompletableFuture .
Tout d'Abord, une monade M
est un foncteur. Autrement dit, il transforme un type en un autre type: Si X
est un type (par exemple String
) alors nous avons un autre type M<X>
(par exemple List<String>
). De plus, si nous avons une transformation/function X -> Y
de types, nous devrions obtenir une fonction M<X> -> M<Y>
.
Mais il y a plus de données à une telle monade. Nous avons une unité dite qui est une 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.
Les données les plus caractéristiques d'une monade, cependant, sont son produit: une fonction M<M<X>> -> M<X>
pour chaque type X
.
Toutes ces données devraient satisfaire certains axiomes comme la fonctorialité, l'associativité, les lois unitaires, mais je n'entrerai pas dans les détails ici et cela n'a pas d'importance non plus pour l'utilisation 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 / objet dans M<X>
peut être liée avec une fonction X -> M<Y>
pour donner une autre valeur dans M<Y>
. Comment pouvons-nous y parvenir? Eh bien, nous appliquons d'abord 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>
. Maintenant, nous pouvons brancher la valeur de M<X>
pour obtenir une valeur dans M<Y>
comme désiré. Cette opération de liaison est utilisée pour enchaîner plusieurs opérations monadiques ensemble.
Passons maintenant à l'exemple CompletableFuture , c'est-à-dire CompletableFuture = M
. Pensez à un objet de CompletableFuture<MyData>
comme un calcul effectué de manière asynchrone et qui donne un objet de MyData
en conséquence dans le futur. Quelles sont les opérations monadiques ici?
- la fonctorialité est réalisée avec la méthode
thenApply
: d'abord le calcul est effectué et dès que le le résultat est disponible, la fonction qui est donné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 la valeur donnée à la fois - le produit monadique n'est pas réalisé par une fonction, mais l'opération de liaison ci-dessous lui est équivalente (avec la fonctorialité) et sa signification sémantique est simplement la suivante: calcul de type
CompletableFuture<CompletableFuture<MyData>>
ce calcul de manière asynchrone donne un autre calcul dansCompletableFuture<MyData>
qui à son tour donne une certaine valeur dansMyData
plus tard, 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 le procédé
thenCompose
Comme vous le voyez, les calculs peuvent maintenant être enveloppé dans un contexte particulier, à savoir asynchronicité. Les structures monadiques générales nous permettent d'enchaîner calculs dans le contexte donné. CompletableFuture
est par exemple utilisé dans le cadre Lagom pour construire facilement des gestionnaires de requêtes hautement asynchrones qui sont 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é).