Monads with Java 8 [JavaLand`16]

Monads with Java 8 [JavaLand`16]

This code-heavy session demystifies what monads are and outlines reasons why you would even want to introduce them into your code. We’ll take a look at the traditional definition for monads and offer a corresponding type definition in Java. We’ve selected a sample Java 8 implementation of a ‘Promise’ monad, which represents the result of async computation to help us answer practical questions about monads. Also, we'll go over the Laws of Monads and show that you can have a proper monad in Java, if you are brave enough to allow the underlying platform change the rules a bit. PS. You won’t be penalised or ridiculed during this session for your (lack of) Haskell knowledge!

5d01eb7205b787b5991db85a11ee5e68?s=128

Oleg Šelajev

March 09, 2016
Tweet

Transcript

  1. Unlocking the Magic of Monads with Java 8 Oleg Šelajev

    @shelajev ZeroTurnaround
  2. @shelajev

  3. http://0t.ee/RLsurvey16 Java Tools and Technologies Survey 2016

  4. None
  5. What do we want to achieve? have fun while learning

    stuff understand the concept of Monad get generic constructs for Monads solve a real-world problem ignore lack of “ad hoc” polymorphism and other slightly relevant facts
  6. What do we want to achieve? have fun while learning

    stuff understand the concept of Monad get generic constructs for Monads solve a real-world problem ignore lack of “ad hoc” polymorphism and other slightly relevant facts
  7. None
  8. Java 8: lambda recap @FunctionalInterface
 public interface Function<T, R> {

    R apply(T t); }
  9. Java 8: lambda recap Function<String, Integer> f = Integer::valueOf;

  10. Java 8: lambda recap String prefix = “JavaDay Kiyv: ";

    
 Function<String, Integer> f = (str) -> {
 System.out.println(prefix + str);
 return str.hashCode();
 };
  11. Death by 1000 tutorials

  12. Problem driven education

  13. Problem statement

  14. Problem statement object.methodCall(arg, () -> { // callback 
 object.methodCall(arg,

    () -> { // callback object.methodCall(arg, () -> { // … });
 });
 });
  15. Problem statement object.interact("option1", () -> {
 object.doThing("fast", () -> {


    if(wasSuccessful()) {
 object.celebrate(100, TimeUnit.SECONDS, () -> {
 System.out.println("Back to work");
 });
 }
 else {
 object.beSad(":(", () -> {
 System.out.println("Still back to work");
 });
 });
 });
  16. Problem statement object.interact("option1", () -> {
 object.doThing("fast", () -> {


    if(wasSuccessful()) {
 object.celebrate(100, TimeUnit.SECONDS, () -> {
 System.out.println("Back to work");
 });
 }
 else {
 object.beSad(":(", () -> {
 System.out.println("Still back to work");
 });
 });
 });
  17. Wishful thinking object.interact("option1")
 .then((o) -> o.doThing("fast"))
 .then((o) -> o.celebrate(100, SECONDS,

    () -> { System.out.println("Back to work”); }))
 .or((o) -> o.beSad(":("));
  18. Wishful thinking object.interact("option1")
 .then((o) -> o.doThing("fast"))
 .then((o) -> o.celebrate(100, SECONDS,

    () -> { System.out.println("Back to work”); }))
 .or((o) -> o.beSad(":("));
  19. Type: async result java.util.concurrent.Future<V>
 boolean isDone(); V get() … V

    get(long timeout, TimeUnit unit)
  20. Type: async result java.util.concurrent.Future<V>
 boolean isDone(); V get() … V

    get(long timeout, TimeUnit unit) Can we do better?
  21. Monads to the rescue

  22. Monads to the rescue

  23. Oh my… a monad in X is just a monoid

    in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
  24. None
  25. http://maciejpirog.github.io/fishy/

  26. Monads: intuition wrapping things chaining functions on those things monad

    is a type
  27. None
  28. Wrapping: return / pure Take instance of “a”, return: “m

    a” Constructor / Factory method
  29. Pure in Java public interface Monad<V> {
 Monad<V> pure(V value);


    }
  30. Chaining: bind / (>>=) take: monad: “m a” function: “a

    => m b” return: monad “m b”
  31. Bind in Java public interface Monad<V> {
 Monad<V> pure(V v);


    <R> Monad<R> bind(Function<V, Monad<R>> f);
 }
  32. Hacking time Promise<V> - result of async computation Kinda like

    Future<V> supports chaining functions: bind
  33. Imagined Promise<V> existing Future operations + finish: p.invoke(V v); add

    callback: p.onRedeem(Action<Promise<V>> callback);
  34. Promise<V>: pure public <V> Promise<V> pure(final V v) {
 Promise<V>

    p = new Promise<>();
 p.invoke(v);
 return p;
 }
  35. Promise<V>: bind public <R> Promise<R> bind(final Function<V, Promise<R>> function) {


    Promise<R> result = new Promise<>();
 this.onRedeem(callback -> {
 V v = callback.get();
 Promise<R> applicationResult = function.apply(v);
 applicationResult.onRedeem(c -> {
 R r = c.get();
 result.invoke(r);
 });
 return result;
 }
  36. Promise<V>: bind public <R> Promise<R> bind(final Function<V, Promise<R>> function) {


    Promise<R> result = new Promise<>();
 this.onRedeem(callback -> {
 V v = callback.get();
 Promise<R> applicationResult = function.apply(v);
 applicationResult.onRedeem(c -> {
 R r = c.get();
 result.invoke(r);
 });
 return result;
 }
  37. Promise<V>: bind public <R> Promise<R> bind(final Function<V, Promise<R>> function) {


    Promise<R> result = new Promise<>();
 this.onRedeem(callback -> {
 V v = callback.get();
 Promise<R> applicationResult = function.apply(v);
 applicationResult.onRedeem(c -> {
 R r = c.get();
 result.invoke(r);
 });
 return result;
 }
  38. Promise<V>: bind public <R> Promise<R> bind(final Function<V, Promise<R>> function) {


    Promise<R> result = new Promise<>();
 this.onRedeem(callback -> {
 V v = callback.get();
 Promise<R> applicationResult = function.apply(v);
 applicationResult.onRedeem(c -> {
 R r = c.get();
 result.invoke(r);
 });
 return result;
 }
  39. Promise<V>: get public V get() throws InterruptedException, ExecutionException {
 if

    (exception != null) {
 throw new ExecutionException(exception);
 }
 return result;
 }
  40. Example Promise<String> p = Async.submit(() -> {
 return "hello world";


    }); 
 Promise<Integer> result = p.bind(string -> Promise.pure(Integer.valueOf(string.hashCode()))); 
 System.out.println("HashCode = " + result.get());
  41. Checkpoint Promise - represents async computation Handling values AND exceptions

    Chaining of functions
  42. Wait, is that it? Monad vs. Instance of monad

  43. Typeclass? Higher functions? Common API Generic functions over all monads

  44. Greatness Common operations for Monads sequence, zip Limited under parametrised

    polymorphism in Java
  45. Greatness Common operations for Monads sequence, zip Sequence of promises

    => joined asynch ops Sequence of elephants => a chain of them
  46. Sequence Monad<List<V>> sequence(Monad<V>... monads);

  47. Greatness Common operations for Monads sequence, zip Limited under parametrised

    polymorphism in Java
  48. Monad in Java public interface Monad<V> {
 Monad<V> pure(V v);


    <R> Monad<R> bind(Function<V, Monad<R> f); 
 V get();
 }
  49. One does not simply call oneself a monad!

  50. None
  51. Laws (don’t be scared) return a >>= f ≡ f

    a m >>= return ≡ m (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
  52. Left identity pure(v).bind(f) ≡ f.apply(v)

  53. Right identity m.bind(m::pure) ≡ m

  54. Associativity m.bind(f).bind(g) ≡ m.bind( (v) -> f.apply(v).bind(g))

  55. Some platforms have it easy Referential transparency Partial application ≡

    is easy
  56. Mortal platforms No referential transparency f.apply(v) != f.apply(v) equals() +

    hashcode()
  57. Defining ≡ for Java Side effects are similar m.get() observes

    the same values values or exceptions
  58. Promise: left identity Function<Integer, Promise<Boolean>> f = (x) -> {


    return submit(() -> x % 2 == 0);
 };
 Integer val = new Integer(100); 
 assertEquals(Promise.pure(val).bind(f).get(), f.apply(val).get());
  59. Promise: right identity Integer val = new Integer(100000);
 Promise<Integer> p

    = Promise.pure(val).bind(Promise::pure); 
 assertEquals(val, p.get());
 assertEquals(identityHashCode(val),
 identityHashCode(p.get()));
  60. Quality software java.util.concurrent.CompletableFuture thenApply(Function / Consumer / etc) thenApplyAsync(Function /

    etc) Async => FJP.common()
  61. Completable Future https://vimeo.com/131394616

  62. None
  63. Optional pure static <T> Optional<T> of(T value) {
 return new

    Optional<>(value);
 } static <T> Optional<T> ofNullable(T value) {
 return value == null ? empty() : of(value);
 }
  64. Optional bind public<U> Optional<U> flatMap(Function<T, Optional<U>> mapper) {
 Objects.requireNonNull(mapper);
 if

    (!isPresent())
 return empty();
 else {
 return Objects.requireNonNull(mapper.apply(value));
 }
 }
  65. Optional bind public<U> Optional<U> map(Function<T, U> mapper) {
 Objects.requireNonNull(mapper);
 if

    (!isPresent())
 return empty();
 else {
 return Optional.ofNullable(mapper.apply(value));
 }
 }
  66. http://maciejpirog.github.io/fishy/

  67. None
  68. None
  69. http://0t.ee/javaland16

  70. oleg@zeroturnaround.com @shelajev github.com/shelajev/promises Contact me

  71. Minority report Alternative definition of Monads: fmap :: (a ->

    b) -> f a -> f b join :: m (m a) -> m a
  72. Exercises Implement (>>=) in terms of fmap and join. Now

    implement join and fmap in terms of (>>=)and return.