Skip to content

Instantly share code, notes, and snippets.

@janojanahan
Created May 31, 2016 10:45
Show Gist options
  • Save janojanahan/50dd01cc7f74ac600448c50b95e7b65b to your computer and use it in GitHub Desktop.
Save janojanahan/50dd01cc7f74ac600448c50b95e7b65b to your computer and use it in GitHub Desktop.
import java.util.Optional;
import java.util.function.Function;
/**
* A Gist prooving that Java 8's Optional type satisfies the Monadic laws
*
* Based on the idea originally by Marc Siegel <marc.siegel@timgroup.com>, at:
* https://gist.github.com/ms-tg/7420496
*
* However, updated to the final/released version of Java 8 (Marcs version was based on pre-release
* versions, which had no lambda notation, and some bugs in the JDK, which had to be worked around)
*
* In Java 8 Optional Monad:
* - of Value T (m T) = Optional<T>
* - return (T -> m T) is: Optional<T>.of()
* - bind (m T >>= (T -> m V) is : Optional<T>.flatmap()
*
* See:
* http://learnyouahaskell.com/a-fistful-of-monads#monad-laws
*
* @author Jano Janahan <jano.janahan@gmail.com>
*/
public class MonadicOptional {
private static Function<Integer, Optional<String>> f =
x -> Optional.of(x.toString());
private static Function<String, Optional<String>> g =
x -> Optional.of(x + " items");
private static Function<Integer, Optional<String>> fThenG =
i -> f.apply(i).flatMap(g);
/**
* Law 1: Left identity
* 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. To put it formally:
*
* return x >>= f is the same damn thing as f x
*/
public static void law11LeftIdentity() {
final Integer value = 2;
Optional<String> lhs = Optional.of(value).flatMap(f);
Optional<String> rhs = f.apply(value);
System.out.println("Law 1: Left Identity : " + (lhs.equals(rhs) ? "applies": "does not apply"));
}
/**
* Law 2: Right Identity
* 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. Formally:
*
* m >>= return is no different than just m
*/
public static void law2RightIdentity() {
Optional<String> monadicValue = Optional.of("A String");
Optional<String> rhs = monadicValue.flatMap(Optional::of);
System.out.println("Law 2: Right Identity : " + (monadicValue.equals(rhs) ? "applies": "does not apply"));
}
/**
* Law 3: Associativity
*
* The final monad law says that when we have a chain of monadic function applications with >>=,
* it shouldn't matter how they're nested. Formally written:
* Doing (m >>= f) >>= g is just like doing m >>= (\x -> f x >>= g)
*/
public static void law3Associativity() {
Optional<Integer> monadicValue = Optional.of(23);
Optional<String> lhs = monadicValue.flatMap(f).flatMap(g);
Optional<String> rhs = monadicValue.flatMap(fThenG);
System.out.println("Law 3: Associativity : " + (lhs.equals(rhs) ? "applies": "does not apply"));
}
public static void main(String[] args) {
law11LeftIdentity();
law2RightIdentity();
law3Associativity();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment