Kann jemand ein Beispiel für die Verwendung von Java geben, um zu verstehen, was eine Monade ist? Sind sie möglich?
Lambda-Ausdrücke sind mit Java möglich, wenn Sie das Lambda-kompatible JDK8 vor der Veröffentlichung von hier herunterladen: http://jdk8.java.net/lambda/
Ein Beispiel für ein Lambda, das dieses JDK verwendet, ist unten dargestellt. Kann jemand eine vergleichsweise einfache Monade bereitstellen?
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);
}
Antworten:
Nur zu deiner Information:
Die vorgeschlagene optionale JDK8- Klasse erfüllt die drei Monadengesetze . Hier ist ein Kern, der das demonstriert .
Eine Monade braucht nur zwei Funktionen, die drei Gesetzen entsprechen.
Die zwei Funktionen:
Platzieren Sie einen Wert in einem monadischen Kontext
return
/Just
Some
Option.some
Optional.of
Wenden Sie eine Funktion im monadischen Kontext an
>>=
(akabind
)flatMap
flatMap
flatMap
In der obigen Übersicht finden Sie eine Java-Demonstration der drei Gesetze.
HINWEIS: Eines der wichtigsten Dinge, die zu verstehen sind, ist die Signatur der Funktion, die im monadischen Kontext angewendet werden soll : Sie verwendet den Rohwerttyp und gibt den monadischen Typ zurück.
Mit anderen Worten, wenn Sie eine Instanz von haben
Optional<Integer>
, haben die Funktionen, die Sie an ihreflatMap
Methode übergeben können, die Signatur(Integer) -> Optional<U>
, wobeiU
es sich um einen Werttyp handelt, der nicht sein mussInteger
, zum BeispielString
: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));
Sie benötigen keine Monadenschnittstelle, um auf diese Weise zu codieren oder so zu denken. In Scala codieren Sie nicht in eine Monad-Schnittstelle (es sei denn, Sie verwenden die Scalaz-Bibliothek ...). Es scheint, dass JDK8 Java-Leuten die Möglichkeit geben wird, diesen Stil verketteter monadischer Berechnungen ebenfalls zu verwenden.
Hoffe das ist hilfreich!
Update: gebloggt dieses hier .
quelle
for
Konstrukt. Monaden sind ohne diese spezielle syntaktische Unterstützung weitaus weniger unterhaltsam.Optional
ist keine echte Monade, danull
Optional
ist eine Monade mitflatMap
dem Bindungsoperator.Java 8 wird Lambdas haben; Monaden sind eine ganz andere Geschichte. Sie sind schwer genug in der funktionalen Programmierung zu erklären (wie die große Anzahl von Tutorials zu diesem Thema in Haskell und Scala zeigt).
Monaden sind ein typisches Merkmal statisch typisierter Funktionssprachen. Um sie in OO-Sprache zu beschreiben, können Sie sich eine
Monad
Schnittstelle vorstellen . Klassen, die implementiert werden,Monad
würden dann als "monadisch" bezeichnet, vorausgesetzt, dass bei der ImplementierungMonad
der Implementierung die sogenannten "Monadengesetze " eingehalten werden. Die Sprache bietet dann etwas syntaktischen Zucker , der die Arbeit mit Instanzen derMonad
Klasse interessant macht.Jetzt
Iterable
in Java hat nichts mit Monaden zu tun, sondern als ein Beispiel für eine Art , dass die Java - Compiler behandelt speziell (dieforeach
Syntax , die mit Java 5 kam), bedenken Sie:Iterable<Something> things = getThings(..); for (Something s: things) { /* do something with s */ }
Während wir also haben könnten verwendet
Iterable
‚sIterator
Methoden (hasNext
und Unternehmen) in einer alten Stilfor
Schleife, Java gewährt uns diesen syntaktischen Zucker als Sonderfall .So wie Klassen, die die Gesetze implementieren
Iterable
undIterator
befolgen müssenIterator
(Beispiel:hasNext
muss zurückgegeben werden,false
wenn es kein nächstes Element gibt), um in derforeach
Syntax nützlich zu sein, würde es mehrere monadische Klassen geben, die mit einer entsprechendendo
Notation nützlich wären (wie sie in Haskell genannt wird) ) oder Scalasfor
Notation.Damit -
In Java 8 weiß ich es nicht - mir ist die Lambda-Notation bekannt, aber mir ist kein anderer spezieller syntaktischer Zucker bekannt, daher muss ich Ihnen ein Beispiel in einer anderen Sprache geben.
Monaden dienen oft als Containerklassen (Listen sind ein Beispiel). Java hat bereits
java.util.List
was offensichtlich nicht monadisch ist, aber hier ist Scalas: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")
Welches ist (grob) syntaktischer Zucker für:
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 //
Dies zeigt eine Funktion von Scala, bei der Monaden genutzt werden, um Listenverständnisse zu vermitteln .
Ein
List
in Scala ist also eine Monade, weil es den Monadengesetzen von Scala entspricht, die vorschreiben, dass alle Monadenimplementierungen konform sein müssenflatMap
,map
undfilter
Methoden (wenn Sie an den Gesetzen interessiert sind, hat der Blogeintrag "Monaden sind Elefanten" die beste Beschreibung I ' habe bisher gefunden). Und wie Sie sehen können, sind Lambdas (und HoF) absolut notwendig, aber nicht ausreichend , um solche Dinge auf praktische Weise nützlich zu machen.Neben den Container-Monaden gibt es auch eine Reihe nützlicher Monaden. Sie haben alle Arten von Anwendungen. Mein Favorit muss die
Option
Monade in Scala sein (dieMaybe
Monade in Haskell), ein Wrapper-Typ, der keine SicherheitOption
bietet : Die Scala-API-Seite für die Monade hat ein sehr einfaches Beispiel: http: //www.scala-lang. org / api / current / scala / Option.html In Haskell sind Monaden nützlich, um E / A darzustellen, um die Tatsache zu umgehen , dass nicht-monadischer Haskell-Code eine unbestimmte Ausführungsreihenfolge hat.Lambdas zu haben ist ein erster kleiner Schritt in die Welt der funktionalen Programmierung. Monaden erfordern sowohl die Monadenkonvention als auch einen ausreichend großen Satz verwendbarer monadischer Typen sowie syntaktischen Zucker, damit die Arbeit mit ihnen Spaß macht und nützlich ist.
Da Scala wohl die Java am nächsten liegende Sprache ist, die auch (monadische) funktionale Programmierung ermöglicht, schauen Sie sich dieses Monad-Tutorial für Scala an, wenn Sie (noch) interessiert sind: http://james-iry.blogspot.jp/2007/09/ Monaden-sind-Elefanten-Teil-1.html
Ein flüchtiges Googeln zeigt, dass es in Java mindestens einen Versuch gibt, dies zu tun: https://github.com/RichardWarburton/Monads-in-Java -
Leider ist das Erklären von Monaden in Java (auch mit Lambdas) genauso schwierig wie das Erklären der vollständigen objektorientierten Programmierung in ANSI C (anstelle von C ++ oder Java).
quelle
Obwohl Monaden in Java implementiert werden können, ist jede Berechnung, die sie betrifft, dazu verdammt, eine chaotische Mischung aus Generika und geschweiften Klammern zu werden.
Ich würde sagen, dass Java definitiv nicht die Sprache ist, die verwendet wird, um ihre Arbeit zu veranschaulichen oder ihre Bedeutung und Essenz zu studieren. Zu diesem Zweck ist es weitaus besser, JavaScript zu verwenden oder einen zusätzlichen Preis zu zahlen und Haskell zu lernen.
Wie auch immer, ich signalisiere Ihnen, dass ich gerade eine Zustandsmonade mit den neuen Java 8-Lambdas implementiert habe . Es ist definitiv ein Haustierprojekt, aber es funktioniert in einem nicht trivialen Testfall.
Sie finden es vielleicht in meinem Blog , aber ich werde Ihnen hier einige Details geben.
Eine Zustandsmonade ist im Grunde eine Funktion von einem Zustand zu einem Paar (Zustand, Inhalt) . Normalerweise geben Sie dem Status einen generischen Typ S und dem Inhalt einen generischen Typ A.
Da Java keine Paare hat, müssen wir sie mit einer bestimmten Klasse modellieren. Nennen wir es Scp (State-Content-Paar), das in diesem Fall einen generischen Typ
Scp<S,A>
und einen Konstruktor hatnew Scp<S,A>(S state,A content)
. Danach können wir sagen, dass die monadische Funktion einen Typ haben wirddas ist ein
@FunctionalInterface
. Das heißt, dass die einzige Implementierungsmethode aufgerufen werden kann, ohne sie zu benennen, wobei ein Lambda-Ausdruck mit dem richtigen Typ übergeben wird.Die Klasse
StateMonad<S,A>
ist hauptsächlich ein Wrapper um die Funktion. Sein Konstruktor kann zB mit aufgerufen werdennew StateMonad<Integer, String>(n -> new Scp<Integer, String>(n + 1, "value"));
Die Zustandsmonade speichert die Funktion als Instanzvariable. Es ist dann notwendig, eine öffentliche Methode bereitzustellen, um darauf zuzugreifen und es dem Staat zuzuführen. Ich habe beschlossen, es zu nennen
s2scp
("State-to-State-Content-Paar").Um die Definition der Monade zu vervollständigen, müssen Sie eine Unit- Methode (auch bekannt als return ) und eine Bind- Methode (auch bekannt als flatMap ) angeben . Persönlich bevorzuge ich es, die Einheit als statisch anzugeben, während bind ein Instanzmitglied ist.
Im Fall der Staatsmonade muss die Einheit wie folgt sein:
public static <S, A> StateMonad<S, A> unit(A a) { return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a)); }
while bind (als Instanzmitglied) ist:
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); }); }
Sie bemerken, dass bind einen generischen Typ B einführen muss, da dies der Mechanismus ist, der die Verkettung heterogener Zustandsmonaden ermöglicht und dieser und jeder anderen Monade die bemerkenswerte Fähigkeit verleiht, die Berechnung von Typ zu Typ zu verschieben.
Ich würde hier mit dem Java-Code aufhören. Das komplexe Zeug befindet sich im GitHub-Projekt. Im Vergleich zu früheren Java-Versionen entfernen Lambdas viele geschweifte Klammern, aber die Syntax ist immer noch ziemlich kompliziert.
Nebenbei zeige ich, wie ähnlicher Status-Monadencode in anderen Mainstream-Sprachen geschrieben werden kann. Im Falle der Scala, bind (die in diesem Fall muss aufgerufen werden flatMap ) liest sich wie
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) })
in der Erwägung, dass die Bindung in JavaScript mein Favorit ist; 100% funktional, schlank und gemein, aber natürlich typlos:
var bind = function(famb){ return state(function(s) { var a = this(s); return famb(a.value)(a.state); }); };
<shameless> Ich schneide hier ein paar Ecken ab, aber wenn Sie an den Details interessiert sind, finden Sie sie auf meinem WP-Blog. </ shameless>
quelle
Die einzige Möglichkeit, Monaden zu verstehen, besteht darin, eine Reihe von Kombinatorbibliotheken zu schreiben, die resultierende Duplizierung zu bemerken und dann selbst herauszufinden, mit Monaden Sie diese Duplizierung herausrechnen können. Wenn man dies entdeckt, baut jeder eine Intuition für das auf, was eine Monade ist… aber diese Intuition kann man nicht direkt mit jemand anderem kommunizieren - es scheint, dass jeder die gleiche Erfahrung machen muss, um aus einem konkreten Grund auf Monaden zu verallgemeinern Beispiele für Kombinatorbibliotheken. jedoch
Hier habe ich einige Materialien gefunden, um Mondas zu lernen.
Ich hoffe, auch für Sie nützlich zu sein.
Codecommit
james-iry.blogspot
debasishg.blogspot
quelle
Monaden sind keine Metaphern von Daniel Spiewak
Monaden in 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()); } }
Vielleicht 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)))); } }
Monade ist ein generisches Muster für die Kapselung verschachtelter Kontrollflüsse. Dh eine Möglichkeit, wiederverwendbare Komponenten aus verschachtelten imperativen Redewendungen zu erstellen.
Es ist wichtig zu verstehen, dass eine Monade nicht nur eine generische Wrapper-Klasse mit einer Flat-Map- Operation ist. Zum Beispiel wird
ArrayList
mit einerflatMap
Methode keine Monade sein. Weil Monadengesetze Nebenwirkungen verbieten.Monade ist ein Formalismus . Es beschreibt die Struktur, unabhängig von Inhalt oder Bedeutung. Menschen haben Schwierigkeiten, sich auf bedeutungslose (abstrakte) Dinge zu beziehen. Sie kommen also auf Metaphern, die keine Monaden sind.
Siehe auch: Gespräch zwischen Erik Meijer und Gilad Bracha.
quelle
fName + " " + middleName()
niemals zurückkehren kannnull
,Optional.ofNullable
was irreführend ist. Wahrscheinlich wolltest duOptional.ofNullable(middleName()).map(mName -> fName + " " + mName)
?Dieser Blog-Beitrag enthält ein schrittweises Beispiel dafür, wie Sie einen Monadentyp (Schnittstelle) in Java implementieren und dann verwenden können, um die Vielleicht-Monade als praktische Anwendung zu definieren.
In diesem Beitrag wird erklärt, dass eine Monade in die Java-Sprache integriert ist, wobei der Punkt hervorgehoben wird, dass Monaden häufiger vorkommen als viele Programmierer denken und dass Codierer sie häufig versehentlich neu erfinden .
quelle
Trotz aller Kontroversen über
Optional
befriedigen, oder nicht, die Monade Gesetze, ich in der Regel wie zu sehenStream
,Optional
undCompletableFuture
in der gleichen Weise. In Wahrheit bieten alle einflatMap()
und das ist alles, was mich interessiert, und lassen mich die " geschmackvolle Zusammensetzung der Nebenwirkungen " (zitiert von Erik Meijer) annehmen. Also wir können entsprechende habenStream
,Optional
undCompletableFuture
in der folgenden Art und Weise:In Bezug auf Monaden vereinfache ich es normalerweise nur beim Nachdenken
flatMap()
(aus dem Kurs " Prinzipien der reaktiven Programmierung " von Erik Meijer):quelle
Ich denke gerne an Monaden, die etwas mathematischer (aber immer noch informeller) sind. Danach werde ich die Beziehung zu einer der Java 8-Monaden CompletableFuture erklären .
Zuallererst ist eine Monade
M
ein Funktor . Das heißt, es wandelt einen Typ in einen anderen Typ um: WennX
es sich um einen Typ handelt (z. B.String
), haben wir einen anderen TypM<X>
(zList<String>
. B. ). Darüber hinaus, wenn wir eine Transformation / Funktion habenX -> Y
von Typen haben, sollten wir außerdem eine Funktion erhaltenM<X> -> M<Y>
.Aber es gibt mehr Daten zu einer solchen Monade. Wir haben eine sogenannte Einheit, die
X -> M<X>
für jeden Typ eine Funktion istX
. Mit anderen Worten, jedes Objekt vonX
kann auf natürliche Weise in die Monade eingewickelt werden.Die charakteristischsten Daten einer Monade sind jedoch das Produkt: eine Funktion
M<M<X>> -> M<X>
für jeden TypX
.Alle diese Daten sollten einige Axiome wie Funktionalität, Assoziativität und Einheitsgesetze erfüllen, aber ich werde hier nicht ins Detail gehen und es spielt auch keine Rolle für den praktischen Gebrauch.
Wir können nun eine andere Operation für Monaden ableiten, die häufig als äquivalente Definition für Monaden verwendet wird, die Bindungsoperation: Ein Wert / Objekt in
M<X>
kann mit einer Funktion gebunden werden, um einenX -> M<Y>
anderen Wert in zu ergebenM<Y>
. Wie erreichen wir das? Nun, zuerst wenden wir Funktionsfunktionalität auf die Funktion an, um eine Funktion zu erhaltenM<X> -> M<M<Y>>
. Als nächstes wenden wir das monadische Produkt auf das Ziel an, um eine Funktion zu erhaltenM<X> -> M<Y>
. Jetzt können wir den Wert von einsteckenM<X>
, um einen Wert in zu erhaltenM<Y>
gewünschten . Diese Bindungsoperation wird verwendet, um mehrere monadische Operationen miteinander zu verketten.Kommen wir nun zum CompletableFuture- Beispiel, d
CompletableFuture = M
. H. Stellen Sie sich ein ObjektCompletableFuture<MyData>
als eine Berechnung vor, die asynchron ausgeführt wird undMyData
als Ergebnis in der Zukunft ein Objekt ergibt . Was sind die monadischen Operationen hier?thenApply
: Zuerst wird die Berechnung durchgeführt und sobald das Ergebnis verfügbar ist, wird die Funktion, die gegebenthenApply
wird, angewendet, um das Ergebnis in einen anderen Typ umzuwandelncompletedFuture
: Wie aus der Dokumentation hervorgeht, ist die resultierende Berechnung bereits abgeschlossen und liefert sofort den angegebenen WertCompletableFuture<CompletableFuture<MyData>>
ergibt diese Berechnung asynchron eine andere Berechnung, beiCompletableFuture<MyData>
der wiederum einige erhalten werden Wert inMyData
später, so dass beide Berechnungen nacheinander durchgeführt werden, ergibt insgesamt eine BerechnungthenCompose
Wie Sie sehen, können Berechnungen jetzt in einem speziellen Kontext zusammengefasst werden, nämlich der Asynchronität . Die allgemeinen monadischen Strukturen ermöglichen es uns, solche Berechnungen im gegebenen Kontext zu verketten.
CompletableFuture
wird beispielsweise im Lagom- Framework verwendet, um auf einfache Weise hochasynchrone Anforderungshandler zu erstellen, die transparent durch effiziente Thread-Pools gesichert werden (anstatt jede Anforderung durch einen dedizierten Thread zu behandeln).quelle
Ein Diagramm für die "optionale" Monade in Java.
Ihre Aufgabe: Führen Sie Operationen an den "Istwerten" (linke Seite) aus, indem Sie Elemente vom Typ
T
Vereinigungnull
in TypU
Vereinigung umwandeln,null
indem Sie die Funktion in der hellblauen Box (die hellblaue Boxfunktion ) verwenden. Hier wird nur ein Kästchen angezeigt, aber möglicherweise befindet sich eine Kette der hellblauen Kästchen (also von TypU
Unionnull
zu TypV
_unionnull
zu TypW
Unionnull
usw.).In der Praxis werden Sie sich Sorgen über
null
Werte machen , die in der Funktionsanwendungskette angezeigt werden. Hässlich!Lösung: Wickeln Sie Ihre mit den hellgrünen Boxfunktionen
T
in eine einOptional<T>
und wechseln Sie zu den "Optionen" (rechte Seite). Transformieren Sie hier Elemente vom Typ in Typ mithilfe der Red-Box-Funktion . In Anlehnung an die Anwendung von Funktionen auf die "Istwerte" können mehrere Red-Box-Funktionen verkettet werden (von Typ zu dann zu usw.). Wechseln Sie am Ende über eine der dunkelgrünen Boxfunktionen von den "Optionen" zu den "Istwerten" zurück .Optional<T>
Optional<U>
Optional<U>
Optional<V>
Optional<W>
Keine Sorge
null
mehr um Werte. In Bezug auf die Implementierung wird es immer eine gebenOptional<U>
, die leer sein kann oder nicht. Sie können die Aufrufe an die Red-Box-Funktionen ketten ohne Nullprüfungen .Der entscheidende Punkt: Die Red-Box-Funktionen werden nicht einzeln und direkt implementiert. Stattdessen werden sie aus den Blue-Box-Funktionen (je nachdem, welche implementiert wurden und verfügbar sind, im Allgemeinen die hellblauen) unter Verwendung der Funktionen
map
oder derflatMap
Funktionen höherer Ordnung erhalten.Die grauen Kästchen bieten zusätzliche Supportfunktionen.
Einfache.
quelle