Created
November 11, 2013 21:14
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ``` | |
* 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 <[email protected]> | |
*/ | |
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)); | |
} | |
} |
@miarocca, you should use more generic variant of Optional::of
- Optional::ofNullable
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)
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
Hi Marc, nice post!
If I'm not mistaken, though, Optional::of does break the the left identity law: