Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Does JDK8's Optional class satisfy the Monad laws? Yes, it does.
/**
* ```
* 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));
}
}

mlarocca commented Oct 6, 2016

Hi Marc, nice post!
If I'm not mistaken, though, Optional::of does break the the left identity law:

    Function f = new Function<String, Optional<String>>() {
        @Override public Optional<String> apply(String str) { return str == null ? Optional.of("X") : Optional.of(str + str); }
    };

    System.out.println(f.apply(null));   // Optional["X"]
    System.out.println(Optional.of(null).flatMap(f));   // throws NPE

saksmt commented Nov 15, 2016

@miarocca, you should use more generic variant of Optional::of - Optional::ofNullable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment