Skip to content

Instantly share code, notes, and snippets.

@ms-tg
Created November 11, 2013 21:14
Show Gist options
  • Save ms-tg/7420496 to your computer and use it in GitHub Desktop.
Save ms-tg/7420496 to your computer and use it in GitHub Desktop.
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
Copy link

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
Copy link

saksmt commented Nov 15, 2016

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

@xgbuils
Copy link

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
Copy link

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