Skip to content

Instantly share code, notes, and snippets.

@ms-tg
Created November 11, 2013 21:14

Revisions

  1. ms-tg created this gist Nov 11, 2013.
    107 changes: 107 additions & 0 deletions jdk8_optional_monad_laws.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,107 @@
    /**
    * ```
    * Does JDK8's Optional class satisfy the Monad laws?
    * =================================================
    * 1. Left identity: true
    * 2. Right identity: true
    * 3. Associativity: true
    *
    * Yes, it does.
    * ```
    *
    * To install the JDK8 Early Access release via Ubuntu PPA, see:
    * http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html
    *
    * For more info on the monad laws, see:
    * [1] http://learnyouahaskell.com/a-fistful-of-monads#monad-laws
    * [2] http://eed3si9n.com/learning-scalaz/Monad+laws.html
    * [3] http://en.wikipedia.org/wiki/Monad_(functional_programming)#Monad_laws
    *
    * NOTE: Code below does *not* use lambdas, because the mainline Java 8 Early
    * Access builds installed by the PPA do not yet include lambda expressions.
    *
    * @author Marc Siegel <marc.siegel@timgroup.com>
    */
    import java.util.function.Function;
    import java.util.Optional;

    class jdk8_optional_monad_laws {
    public static void main (String[] args) throws java.lang.Exception
    {
    System.out.println("");
    System.out.println("Does JDK8's Optional class satisfy the Monad laws?");
    System.out.println("=================================================");
    System.out.println(" 1. Left identity: " + satisfiesLaw1LeftIdentity());
    System.out.println(" 2. Right identity: " + satisfiesLaw2RightIdentity());
    System.out.println(" 3. Associativity: " + satisfiesLaw3Associativity());
    System.out.println("");
    System.out.println(satisfiesLaw1LeftIdentity()
    && satisfiesLaw2RightIdentity()
    && satisfiesLaw3Associativity()
    ? "Yes, it does."
    : "No, it doesn't.");
    }

    // Input values for the monad law tests below
    static int value = 42;
    static Optional monadicValue = Optional.of(value);

    // With lambdas, this entire thing goes away (pass `Optional.of` directly)
    static Function optionalOf = new Function<Integer, Optional<Integer>>() {
    @Override public Optional<Integer> apply(Integer n) { return Optional.of(n); }
    };

    // With lambdas, this becomes `n -> Optional.of(n * 2)`
    static Function f = new Function<Integer, Optional<Integer>>() {
    @Override public Optional<Integer> apply(Integer n) { return Optional.of(n * 2); }
    };

    // With lambdas, this becomes `n -> Optional.of(n * 5)`
    static Function g = new Function<Integer, Optional<Integer>>() {
    @Override public Optional<Integer> apply(Integer n) { return Optional.of(n * 5); }
    };

    // With lambdas, this becomes `n -> f(n).flatMap(g)`
    static Function f_flatMap_g = new Function<Integer, Optional<Integer>>() {
    @Override public Optional<Integer> apply(Integer n) { return ((Optional<Integer>) f.apply(n)).flatMap(g); }
    // NOTE (2013-11-11): Bug in latest JDK8 requires this cast: ^^^^^^^^^^^^^^^^^^^
    };


    /**
    * Monad law 1, Left Identity
    *
    * From LYAHFGG [1] above:
    * The first monad law states that if we take a value, put it in a default context
    * with return and then feed it to a function by using >>=, it’s the same as just
    * taking the value and applying the function to it
    */
    public static boolean satisfiesLaw1LeftIdentity() {
    return Optional.of(value).flatMap(f)
    .equals(f.apply(value));
    }

    /**
    * Monad law 2, Right Identity
    *
    * From LYAHFGG [1] above:
    * The second law states that if we have a monadic value and we use >>= to feed
    * it to return, the result is our original monadic value.
    */
    public static boolean satisfiesLaw2RightIdentity() {
    return monadicValue.flatMap(optionalOf)
    .equals(monadicValue);
    }

    /**
    * Monad law 3, Associativity
    *
    * From LYAHFGG [1] above:
    * The final monad law says that when we have a chain of monadic function
    * applications with >>=, it shouldn’t matter how they’re nested.
    */
    public static boolean satisfiesLaw3Associativity() {
    return monadicValue.flatMap(f).flatMap(g)
    .equals(monadicValue.flatMap(f_flatMap_g));
    }
    }