Monads with Java 8

前端 未结 9 1032
礼貌的吻别
礼貌的吻别 2020-12-07 07:29

In the interests of helping to understand what a monad is, can someone provide an example using java ? Are they possible ?

Lambda expressions are possible using java

相关标签:
9条回答
  • 2020-12-07 08:01

    Just FYI:

    The proposed JDK8 Optional class does satisfy the three Monad laws. Here's a gist demonstrating that.

    All it takes be a Monad is to provide two functions which conform to three laws.

    The two functions:

    1. Place a value into monadic context

      • Haskell's Maybe: return / Just
      • Scala's Option: Some
      • Functional Java's Option: Option.some
      • JDK8's Optional: Optional.of
    2. Apply a function in monadic context

      • Haskell's Maybe: >>= (aka bind)
      • Scala's Option: flatMap
      • Functional Java's Option: flatMap
      • JDK8's Optional: flatMap

    Please see the above gist for a java demonstration of the three laws.

    NOTE: One of the key things to understand is the signature of the function to apply in monadic context: it takes the raw value type, and returns the monadic type.

    In other words, if you have an instance of Optional<Integer>, the functions you can pass to its flatMap method will have the signature (Integer) -> Optional<U>, where U is a value type which does not have to be Integer, for example 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));
    

    You don't need any sort of Monad Interface to code this way, or to think this way. In Scala, you don't code to a Monad Interface (unless you are using Scalaz library...). It appears that JDK8 will empower Java folks to use this style of chained monadic computations as well.

    Hope this is helpful!

    Update: Blogged about this here.

    0 讨论(0)
  • 2020-12-07 08:15

    the only way to understand monads is by writing a bunch of combinator libraries, noticing the resulting duplication, and then discovering for yourself that monads let you factor out this duplication. In discovering this, everyone builds some intuition for what a monad is… but this intuition isn’t the sort of thing that you can communicate to someone else directly – it seems everyone has to go through the same experience of generalizing to monads from some concrete examples of combinator libraries. however

    here i found some materials to learn Mondas.

    hope to be useful for you too.

    codecommit

    james-iry.blogspot

    debasishg.blogspot

    0 讨论(0)
  • 2020-12-07 08:17

    Java 8 will have lambdas; monads are a whole different story. They are hard enough to explain in functional programming (as evidenced by the large number of tutorials on the subject in Haskell and Scala).

    Monads are a typical feature of statically typed functional languages. To describe them in OO-speak, you could imagine a Monad interface. Classes that implement Monad would then be called 'monadic', provided that in implementing Monad the implementation obeys what are known as the 'monad laws'. The language then provides some syntactic sugar that makes working with instances of the Monad class interesting.

    Now Iterable in Java has nothing to do with monads, but as a example of a type that the Java compiler treats specially (the foreach syntax that came with Java 5), consider this:

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

    So while we could have used Iterable's Iterator methods (hasNext and company) in an old-style for loop, Java grants us this syntactic sugar as a special case.

    So just as classes that implement Iterable and Iterator must obey the Iterator laws (Example: hasNext must return false if there is no next element) to be useful in foreach syntax - there would exist several monadic classes that would be useful with a corresponding do notation (as it is called in Haskell) or Scala's for notation.

    So -

    1. What are good examples of monadic classes?
    2. What would syntactic sugar for dealing with them look like?

    In Java 8, I don't know - I am aware of the lambda notation but I am not aware of other special syntactic sugar, so I'll have to give you an example in another language.

    Monads often serve as container classes (Lists are an example). Java already has java.util.List which is obviously not monadic, but here is Scala's:

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

    Which is (roughly) syntactic sugar for:

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

    This shows a feature of Scala where monads are exploited to provide list comprehensions.

    So a List in Scala is a monad, because it obeys Scala's monad laws, which stipulate that all monad implementations must have conforming flatMap, map and filter methods (if you are interested in the laws, the "Monads are Elephants" blog entry has the best description I've found so far). And, as you can see, lambdas (and HoF) are absolutely necessary but not sufficient to make this kind of thing useful in a practical way.

    There's a bunch of useful monads besides the container-ish ones as well. They have all kinds of applications. My favorite must be the Option monad in Scala (the Maybe monad in Haskell), which is a wrapper type which brings about null safety: the Scala API page for the Option monad has a very simple example usage: http://www.scala-lang.org/api/current/scala/Option.html In Haskell, monads are useful in representing IO, as a way of working around the fact that non-monadic Haskell code has indeterminate order of execution.

    Having lambdas is a first small step into the functional programming world; monads require both the monad convention and a large enough set of usable monadic types, as well as syntactic sugar to make working with them fun and useful.

    Since Scala is arguably the language closest to Java that also allows (monadic) Functional Programming, do look at this Monad tutorial for Scala if you are (still) interested: http://james-iry.blogspot.jp/2007/09/monads-are-elephants-part-1.html

    A cursory googling shows that there is at least one attempt to do this in Java: https://github.com/RichardWarburton/Monads-in-Java -

    Sadly, explaining monads in Java (even with lambdas) is as hard as explaining full-blown Object oriented programming in ANSI C (instead of C++ or Java).

    0 讨论(0)
  • 2020-12-07 08:21

    Here's the thing about monads which is hard to grasp: monads are a pattern, not a specific type. Monads are a shape, they are an abstract interface (not in the Java sense) more than they are a concrete data structure. As a result, any example-driven tutorial is doomed to incompleteness and failure. [...] The only way to understand monads is to see them for what they are: a mathematical construct.

    Monads are not metaphors by Daniel Spiewak


    Monads in Java SE 8

    List monad

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

    Maybe monad

    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 is a generic pattern for nested control flow encapsulation. I.e. a way to create reusable components from nested imperative idioms.

    Important to understand that a monad is not just a generic wrapper class with a flat map operation. For example, ArrayList with a flatMap method won't be a monad. Because monad laws prohibit side effects.

    Monad is a formalism. It describes the structure, regardless of content or meaning. People struggle with relating to meaningless (abstract) things. So they come up with metaphors which are not monads.

    See also: conversation between Erik Meijer and Gilad Bracha.

    0 讨论(0)
  • 2020-12-07 08:21

    I like to think of monads in slighlty more mathematical (but still informal) fashion. After that I will explain the relationship to one of Java 8's monads CompletableFuture.

    First of all, a monad M is a functor. That is, it transforms a type into another type: If X is a type (e.g. String) then we have another type M<X> (e.g. List<String>). Moreover, if we have a transformation/function X -> Y of types, we should get a function M<X> -> M<Y>.

    But there is more data to such a monad. We have a so-called unit which is a function X -> M<X> for each type X. In other words, each object of X can be wrapped in a natural way into the monad.

    The most characteristic data of a monad, however, is it's product: a function M<M<X>> -> M<X> for each type X.

    All of these data should satisfy some axioms like functoriality, associativity, unit laws, but I won't go into detail here and it also doesn't matter for practical usage.

    We can now deduce another operation for monads, which is often used as an equivalent definition for monads, the binding operation: A value/object in M<X> can be bound with a function X -> M<Y> to yield another value in M<Y>. How do we achieve this? Well, first we apply functoriality to the function to obtain a function M<X> -> M<M<Y>>. Next we apply the monadic product to the target to obtain a function M<X> -> M<Y>. Now we can plug in the value of M<X> to obtain a value in M<Y> as desired. This binding operation is used to chain several monadic operations together.

    Now lets come to the CompletableFuture example, i.e. CompletableFuture = M. Think of an object of CompletableFuture<MyData> as some computation that's performed asynchronously and which yields an object of MyData as a result some time in the future. What are the monadic operations here?

    • functoriality is realized with the method thenApply: first the computation is performed and as soon as the result is available, the function which is given to thenApply is applied to transform the result into another type
    • the monadic unit is realized with the method completedFuture: as the documentation tells, the resulting computation is already finished and yields the given value at once
    • the monadic product is not realized by a function, but the binding operation below is equivalent to it (together with functoriality) and its semantic meaning is simply the following: given a computation of type CompletableFuture<CompletableFuture<MyData>> that computation asynchronously yields another computation in CompletableFuture<MyData> which in turn yields some value in MyData later on, so performing both computations on after the other yields one computation in total
    • the resulting binding operation is realized by the method thenCompose

    As you see, computations can now be wrapped up in a special context, namely asynchronicity. The general monadic structures enable us to chain such computations in the given context. CompletableFuture is for example used in the Lagom framework to easily construct highly asynchronous request handlers which are transparently backed up by efficient thread pools (instead of handling each request by a dedicated thread).

    0 讨论(0)
  • 2020-12-07 08:23

    Even though monads can be implemented in Java, any computation involving them is doomed to become a messy mix of generics and curly braces.

    I'd say that Java is definitely not the language to use in order to illustrate their working or to study their meaning and essence. For this purpose it is far better to use JavaScript or to pay some extra price and learn Haskell.

    Anyway, I am signaling you that I just implemented a state monad using the new Java 8 lambdas. It's definitely a pet project, but it works on a non-trivial test case.

    You may find it presented at my blog, but I'll give you some details here.

    A state monad is basically a function from a state to a pair (state,content). You usually give the state a generic type S and the content a generic type A.

    Because Java does not have pairs we have to model them using a specific class, let's call it Scp (state-content pair), which in this case will have generic type Scp<S,A> and a constructor new Scp<S,A>(S state,A content). After doing that we can say that the monadic function will have type

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

    which is a @FunctionalInterface. That's to say that its one and only implementation method can be invoked without naming it, passing a lambda expression with the right type.

    The class StateMonad<S,A> is mainly a wrapper around the function. Its constructor may be invoked e.g. with

    new StateMonad<Integer, String>(n -> new Scp<Integer, String>(n + 1, "value"));
    

    The state monad stores the function as an instance variable. It is then necessary to provide a public method to access it and feed it the state. I decided to call it s2scp ("state to state-content pair").

    To complete the definition of the monad you have to provide a unit (aka return) and a bind (aka flatMap) method. Personally I prefer to specify unit as static, whereas bind is an instance member.

    In the case of the state monad, unit gotta be the following:

    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 (as instance member) is:

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

    You notice that bind must introduce a generic type B, because it is the mechanism that allows the chaining of heterogeneous state monads and gives this and any other monad the remarkable capability to move the computation from type to type.

    I'd stop here with the Java code. The complex stuff is in the GitHub project. Compared to previous Java versions, lambdas remove a lot of curly braces, but the syntax is still pretty convoluted.

    Just as an aside, I'm showing how similar state monad code may be written in other mainstream languages. In the case of Scala, bind (which in that case must be called flatMap) reads like

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

    whereas the bind in JavaScript is my favorite; 100% functional, lean and mean but -of course- typeless:

    var bind = function(famb){
        return state(function(s) {
            var a = this(s);
            return famb(a.value)(a.state);
        });
    };
    

    <shameless> I am cutting a few corners here, but if you are interested in the details you will find them on my WP blog.</shameless>

    0 讨论(0)
提交回复
热议问题