Instantly share code, notes, and snippets.

Embed
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

This comment has been minimized.

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

This comment has been minimized.

saksmt commented Nov 15, 2016

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

@xgbuils

This comment has been minimized.

xgbuils commented Jan 23, 2018

Optional.ofNullable & Optional.flatMap neither satisfy the monad laws.

Function<T, Boolean> f = x -> Optional.of(x == null)
Optional.ofNullable(null).flatMap(f) is not equivalent to f(null)

@jakubgwozdz

This comment has been minimized.

jakubgwozdz commented May 3, 2018

That's because null is just null, not an instance of the Boolean, String, or whatever.
That's why we need to go away from Java as soon as possible. All hail Kotlin :)

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