wake-up-neo.com

Monaden mit Java 8

Kann jemand mit Hilfe von Java ein Beispiel 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 mit Vorabversion von hier herunterladen. Http://jdk8.Java.net/lambda/

Ein Beispiel für ein Lambda, das dieses JDK verwendet, kann unten gezeigt werden. 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);
    }
74
NimChimpsky

Nur zur Info:

Die vorgeschlagene JDK8 Optional -Klasse erfüllt die drei Monadengesetze . Hier ist ein Gist demonstriert das.

Eine Monade genügt, um zwei Funktionen bereitzustellen, die drei Gesetzen entsprechen.

Die zwei Funktionen:

  1. Platzieren Sie einen -Wert im monadischen Kontext.

    • Haskells Vielleicht: returnJust
    • Option von Scala: Some
    • Funktionale Java-Option: Option.some
    • JDK8s optional: Optional.of
  2. Anwenden einer -Funktion in monadischem Kontext

    • Haskells Vielleicht: >>= (aka bind)
    • Option von Scala: flatMap
    • Funktionale Java-Option: flatMap
    • JDK8s Optional: flatMap

Eine Java-Demonstration der drei Gesetze finden Sie im über Gist

HINWEIS: Eines der wichtigsten Dinge, die Sie verstehen müssen, ist die Signatur der Funktion , die im monadischen Kontext angewendet werden soll: Sie nimmt den Rohwerttyp und gibt den Monadentyp zurück. 

Wenn Sie also über eine Instanz von Optional<Integer> verfügen, haben die Funktionen, die Sie an ihre flatMap-Methode übergeben können, die Signatur (Integer) -> Optional<U>, wobei U ein Werttyp ist, der nicht Integer sein muss, beispielsweise 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));

Sie brauchen keine Monad-Schnittstelle, um auf diese Weise zu codieren oder auf diese Weise zu denken. In Scala codieren Sie nicht in eine Monad-Benutzeroberfläche (es sei denn, Sie verwenden die Scalaz-Bibliothek ...). Es scheint, dass JDK8 Java-Leuten die Möglichkeit geben wird, diesen Stil von verketteten monadischen Berechnungen zu verwenden.

Hoffe das ist hilfreich!

_/Update: Blogging über dieses hier .

74
ms-tg

Java 8 wird Lambdas haben; Monaden sind eine ganz andere Geschichte. Sie sind schwer genug in der funktionalen Programmierung zu erklären (wie die zahlreichen Tutorials zu diesem Thema in Haskell und Scala belegen).

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 Monad implementieren, werden dann als "monadisch" bezeichnet, vorausgesetzt, dass bei der Implementierung von Monad die Implementierung den sogenannten "Monadengesetzen" gehorcht. Die Sprache stellt dann einige syntaktische Zucker bereit, die das Arbeiten mit Instanzen der Monad-Klasse interessant machen.

Nun hat Iterable in Java nichts mit Monaden zu tun, aber als Beispiel für einen Typ, den der Java-Compiler speziell behandelt (die mit Java 5 gelieferte foreach-Syntax), sollten Sie Folgendes berücksichtigen:

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

Während wir Iterables Iterator-Methoden (hasNext und company) in einer alten for-Schleife hätten verwenden können, räumt uns Java diesen syntaktischen Zucker als Sonderfall ein.

So wie Klassen, die Iterable und Iterator implementieren, die Iterator-Gesetze einhalten müssen (Beispiel: hasNext muss false zurückgeben, wenn kein nächstes Element vorhanden ist), um in foreach-Syntax nützlich zu sein - da wären mehrere monadic-Klassen vorhanden nützlich mit einer entsprechenden do-Notation (wie sie in Haskell genannt wird) oder Scalas for-Notation.

So -

  1. Was sind gute Beispiele für monadische Klassen?
  2. Wie würde syntaktischer Zucker für den Umgang damit aussehen?

In Java 8 weiß ich es nicht - ich kenne die Lambda-Notation, aber ich kenne keinen anderen speziellen syntaktischen Zucker, daher muss ich Ihnen ein Beispiel in einer anderen Sprache geben.

Monaden dienen oft als Container-Klassen (Listen sind ein Beispiel). Java hat Java.util.List bereits, 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 (ungefähr) 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 ausgenutzt werden, um List Comprehensions bereitzustellen.

Eine List in Scala ist also eine Monade, weil sie Scalas Monadengesetzen gehorcht, die besagen, dass alle Monadenimplementierungen über übereinstimmende flatMap-, map- und filter-Methoden verfügen müssen (wenn Sie an den Gesetzen interessiert sind, enthält der Blogeintrag "Monaden sind Elefanten") beste Beschreibung, die ich bisher gefunden habe). Und wie Sie sehen können, sind Lambdas (und HoF) absolut notwendig, aber nicht ausreichend, um diese Dinge auf praktische Weise nützlich zu machen.

Neben den containerartigen gibt es eine Reihe nützlicher Monaden. Sie haben alle Arten von Anwendungen. Mein Favorit muss die Option-Monade in Scala (die Maybe-Monad in Haskell) sein. Dies ist ein Wrapper-Typ, der null safety bewirkt: Die Scala-API-Seite für die Option-Monad hat ein sehr einfaches Beispiel: http://www.scala-lang.org/api/current/scala/Option.html In Haskell sind Monaden nützlich, um IO darzustellen, um die Tatsache zu umgehen, dass nicht-monadisches Haskell Code hat eine unbestimmte Reihenfolge der Ausführung.

Lambdas zu haben, ist ein erster kleiner Schritt in die Welt der funktionalen Programmierung. monads erfordern sowohl die monad-Konvention als auch eine ausreichend große Anzahl verwendbarer monadischer Typen, sowie syntaktischer Zucker, um die Arbeit mit ihnen lustiger und nützlicher zu machen.

Da Scala wohl die Sprache ist, die Java am nächsten kommt und die (monadische) funktionale Programmierung erlaubt, sollten Sie sich dieses Monad-Tutorial für Scala ansehen, wenn Sie (noch) interessiert sind: http://james-iry.blogspot.jp /2007/09/monads-are-elephants-part-1.html

Ein flüchtiges Googeln zeigt, dass es mindestens einen Versuch gibt, dies in Java zu tun: https://github.com/RichardWarburton/Monads-in-Java -

Leider ist das Erklären von Monaden in Java (sogar mit Lambdas) genauso schwierig wie das Erklären von objektorientierter Programmierung in ANSI C (anstelle von C++ oder Java).

55
Faiz

Obwohl Monaden in Java implementiert werden können, ist jede Berechnung, die sie einbezieht, dazu verurteilt, eine chaotische Mischung aus Generika und geschweiften Klammern zu werden.

Ich würde sagen, dass Java ist definitiv nicht die Sprache, um ihre Arbeit zu veranschaulichen oder ihre Bedeutung und Essenz zu studieren Zu diesem Zweck ist es weitaus besser, JavaScript zu verwenden oder einen Aufpreis 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 Lieblingsprojekt, aber es funktioniert in einem nicht trivialen Testfall.

Sie finden es vielleicht unter mein Blog , aber ich gebe Ihnen hier einige Details.

Eine Zustandsmonade ist im Grunde eine Funktion von einem Zustand zu einem Paar (Zustand, Inhalt) . Normalerweise geben Sie dem Zustand 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 pair), was in diesem Fall der Fall ist habe den generischen Typ Scp<S,A> und einen Konstruktor new Scp<S,A>(S state,A content). Danach können wir sagen, dass die monadische Funktion den Typ haben wird

Java.util.function.Function<S,Scp<S,A>>

das ist ein @FunctionalInterface. Das heißt, dass die einzige Implementierungsmethode aufgerufen werden kann, ohne sie zu benennen. Dabei wird ein Lambda-Ausdruck mit dem richtigen Typ übergeben.

Die Klasse StateMonad<S,A> Ist hauptsächlich ein Wrapper um die Funktion. Sein Konstruktor kann z.B. mit

new 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 den Zustand zu speisen. Ich habe beschlossen, es s2scp ("State to state-content pair") zu nennen.

Um die Definition der Monade zu vervollständigen, müssen Sie eine unit (aka return) und eine bind (aka = angeben flatMap) Methode. Persönlich bevorzuge ich die Angabe der Einheit als statisch, während bind ein Instanzmitglied ist.

Im Falle der Staatsmonade muss die Einheit die folgende 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.

Abgesehen davon zeige ich, wie ähnlich der Code für staatliche Monaden in anderen gängigen Sprachen geschrieben werden kann. Im Falle von Scala lautet bind (was in diesem Fall heißen muss flatMap) 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)
})

während das Binden in JavaScript mein Favorit ist; 100% funktional, schlank und gemein, aber natürlich typenlos:

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, werden Sie sie auf meinem WP Blog. </ shameless> finden

10

Die Sache über Monaden ist schwer zu verstehen: Monaden sind eine Muster, kein bestimmter Typ. Monaden sind eine Form, sie sind eine abstrakte Schnittstelle (nicht im Java-Sinn) mehr als sie konkrete Daten sind Struktur. Infolgedessen ist jedes beispielorientierte Lernprogramm zu .__ verurteilt. Unvollständigkeit und Versagen . [...] Die einzige Möglichkeit, Monaden zu verstehen, besteht darin, sie als das zu sehen, was sie sind: ein mathematisches Konstrukt.

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 eine 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 verschachtelte Steuerflusskapselung . I.e. eine Möglichkeit, wiederverwendbare Komponenten aus verschachtelten imperativen Idiomen zu erstellen .

Es ist wichtig zu verstehen, dass eine Monade nicht nur eine generische Wrapper-Klasse mit einer flachen Karte -Operation ist. Zum Beispiel wäre ArrayList mit einer flatMap-Methode keine Monade . Weil Monadengesetze Nebenwirkungen unterbinden.

Monade ist ein Formalismus. Es beschreibt die Struktur, unabhängig von Inhalt oder Bedeutung. Die Menschen kämpfen mit der Beziehung zu sinnlosen (abstrakten) Dingen. Sie kommen also mit Metaphern, die keine Monaden sind.

Siehe auch:Gespräch zwischen Erik Meijer und Gilad Bracha. 

4
user2418306

die einzige Möglichkeit, Monaden zu verstehen, besteht darin, eine Reihe von Kombinatorbibliotheken zu schreiben, die resultierende Duplizierung zu bemerken und dann selbst herauszufinden, dass Monaden Sie diese Duplikation ausgleichen lassen. Bei der Entdeckung dieses Sachverhaltes baut jeder eine gewisse Intuition für das auf, was eine Monade ist… aber diese Intuition ist nicht die Art von Ding, die Sie direkt mit jemandem kommunizieren können - es scheint, als müsste jeder die gleiche Erfahrung machen, Monads aus etwas Konkretem zu machen Beispiele für Kombinatorbibliotheken. jedoch

hier habe ich Materialien gefunden, um Mondas zu lernen.

ich hoffe, auch für Sie nützlich zu sein.

codecommit

james-iry.blogspot

debasishg.blogspot

4
Morteza Adi

Dieser Blogbeitrag enthält ein schrittweises Beispiel, wie Sie einen Monad-Typ (eine Schnittstelle) in Java implementieren und dann verwenden können, um die vielleicht-Monad als praktische Anwendung zu definieren.

Dieser Beitrag erklärt, dass es eine Monade in der Java-Sprache gibt, die den Punkt betont, dass Monaden häufiger sind, als viele Programmierer meinen, und dass Codierer oft ungewollt neu erfinden .

2
Katie J. Ots

Ich denke gerne an Monaden auf etwas mathematischere (aber immer noch informelle) Weise. Danach erkläre ich die Beziehung zu einem der Monaden von Java 8 CompletableFuture .

Zuallererst ist eine Monade M ein functor . Das heißt, es wandelt einen Typ in einen anderen Typ um: Wenn X ein Typ ist (z. B. String), dann haben wir einen anderen Typ M<X> (z. B. List<String>). Wenn wir außerdem eine Transformation/Funktion X -> Y von Typen haben, sollten wir eine Funktion M<X> -> M<Y> bekommen.

Es gibt jedoch mehr Daten zu einer solchen Monade. Wir haben eine sogenannte Einheit, die für jeden Typ X eine Funktion X -> M<X> ist. Mit anderen Worten, jedes Objekt von X kann auf natürliche Weise in die Monade eingeschlossen werden.

Die charakteristischsten Daten einer Monade sind jedoch ihr Produkt: eine Funktion M<M<X>> -> M<X> für jeden Typ X.

Alle diese Daten sollten einige Axiome wie Funktionalität, Assoziativität, Einheitengesetze erfüllen, aber ich werde hier nicht ins Detail gehen und es spielt für die praktische Anwendung keine Rolle.

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 X -> M<Y> gebunden werden, um einen anderen Wert in M<Y> zu erhalten. Wie erreichen wir das? Nun, zuerst wenden wir die Funktionalität an, um eine Funktion M<X> -> M<M<Y>> zu erhalten. Als Nächstes wenden wir das monadische Produkt auf das Ziel an, um eine Funktion M<X> -> M<Y> zu erhalten. Jetzt können wir den Wert von M<X> anschließen, um einen Wert in M<Y> nach Wunsch zu erhalten. Diese Bindungsoperation wird verwendet, um mehrere monadische Operationen miteinander zu verketten.

Kommen wir nun zum CompletableFuture - Beispiel, d. H. CompletableFuture = M. Stellen Sie sich ein Objekt von CompletableFuture<MyData> als eine asynchron durchgeführte Berechnung vor, die zu einem späteren Zeitpunkt ein Objekt von MyData zur Folge hat. Was sind die monadischen Operationen hier?

  • functoriality wird mit der Methode thenApply realisiert: Zuerst wird die Berechnung durchgeführt und sobald das Ergebnis verfügbar ist, wird die Funktion, die an thenApply übergeben wird, angewendet, um das Ergebnis in einen anderen Typ umzuwandeln
  • die monadische Einheit wird mit der Methode completedFuture realisiert: Wie aus der Dokumentation hervorgeht, ist die resultierende Berechnung bereits abgeschlossen und liefert sofort den angegebenen Wert
  • das monadische Produkt wird nicht durch eine Funktion realisiert, aber die folgende Bindungsoperation ist (zusammen mit der Funktionalität) äquivalent, und seine semantische Bedeutung ist einfach die folgende: Bei einer Berechnung des Typs CompletableFuture<CompletableFuture<MyData>> ergibt diese Berechnung asynchron eine weitere Berechnung in CompletableFuture<MyData>, die wiederum asynchron eine andere Berechnung ergibt ergibt später einen Wert in MyData, so dass die Ausführung beider Berechnungen nach den anderen Berechnungen insgesamt eine Berechnung ergibt
  • die resultierende Bindungsoperation wird durch die Methode thenCompose realisiert.

Wie Sie sehen, können Berechnungen nun in einem speziellen Kontext zusammengefasst werden, nämlich Asynchronität . Die allgemeinen monadischen Strukturen ermöglichen es, solche Berechnungen im gegebenen Kontext zu verketten. CompletableFuture wird beispielsweise im Lagom - Framework verwendet, um auf einfache Weise hoch asynchrone Anforderungshandler zu erstellen, die transparent durch effiziente Threadpools gesichert werden (anstatt jede Anfrage durch einen dedizierten Thread zu behandeln).

0
Werner Thumann

Trotz aller Kontroversen darüber, ob Optional die Monadengesetze erfüllt oder nicht, sehe ich Stream, Optional und CompletableFuture normalerweise genauso. In Wahrheit bieten sie alle eine flatMap() und das ist alles, was mich interessiert, und lassen Sie mich die " die geschmackvolle Zusammensetzung der Nebenwirkungen " (zitiert von Erik Meijer) annehmen. So können wir entsprechende Stream, Optional und CompletableFuture auf folgende Weise haben:

  

In Bezug auf Monaden vereinfache ich es normalerweise, indem ich nur an flatMap() denke (aus dem Kurs " Principles of Reactive Programming " von Erik Meijer):

Eric-Meijer-flatMap

0
Miguel Gamboa