Monads for Dummies

Programmers

What's a Monad?

In a nutshell, Monads, as a programming concept,
add a layer of indirection
to functions calls

That level of indirection gives you lots of possible advantages including
safely reacting to errors

That idea is not new, what it is new with Monads is the idea of Composability

B foo(A a) { /* code */ }
C bar(B b) { /* code */ }

bar(foo(someA))

Composability for normal methods

Composability

Means that if I have a function A -> B and another B -> C then implicitly
I have one A -> C 

Monads let you easily
chain function calls
without leaving the possibility of indirection

How does that
relate to code?

A Monad wraps a value

There are a lot of metaphors you can use for this: a box, a burrito, etc.

In Java the best
representation is a parametized type: Monad<T>

Once you have a
Monad, you can pass functions that the Monad will apply to the value

The trick to chaining is
that these operations return always a Monad, but maybe of a different type

So if I start with a Monad<T> and I have a function T -> U, after
the Monad applies f
to the value what
it returns Monad<U>

So you are free
to chain lots of
operations without
"leaving the Monad"

The most important operations of a Monad are: unit and bind / map

unit - given a value returns a Monad wrapping it;
in the OOP world
possibly a Constructor
or a Static Factory 

bind / map - given an function, apply the result
to the value but return
it wrapped in a Monad

The others common
Monad methods
are just icing
on the cake! 

Let's implement
the simplest
Monad possible:
the Identity Monad

import java.util.function.Function;

public interface Monad<T> {
    <U> Monad<U> map(Function<T, U> mapper);
    T get(); // unwrap value
}
public class Identity<T> implements Monad<T> {
  private T value;
  private Identity(T value) { this.value = value; }

  // for a Static Factory, of() is a 
  // better name than unit()
  public static <T> Identity<T> of(T value) {
    return new Identity<T>(value);
  }
  // map can act as a decorator of mapper
  public <U> Identity<U> map(Function<T, U> mapper) {
    U mapperValue = mapper.apply(value);
    return Identity.of(mapperValue); // can't escape the Monad!
  }

  public T get() {
    return value;
  }
}

That's it! now lets
use our Monad

Brief Reminder of
Lambda Expressions

  • Lambdas are just annonymous functions
  • An -> separates arguments
    and body of the function
  • Usually you don't specify types
    (they are inferred)
  • For the body, usually you don't need
    {}, a return statement or semicolons.

Brief Reminder of
Method Reference

  • If the name of the arguments is not important you can use a Method Reference that usually looks like: Class::methodName
  • For example in the lambda expression
    s -> s.toUppercase, the name of the argument is not important so you can use String::toUppercase
System.out.println(
  Identity.of("Monads are fun")
          .map(String::toUppercase)
          .map(s -> s.replace(" ", "-"))
          .get()
); // prints MONADS-ARE-FUN
"Monads are Fun".toUppercase().replace(" ", "-");
System.out.println(
  Identity.of("Monads are fun") // <String>
          .map(s -> s.replace(" ", ""))
          .map(String::length) // <Integer>
          .get();
); // prints 12
"Monads are Fun".replace(" ", "").length();

Granted, we are not
taking advantage of
the indirection, but you should see the potential!

What's up with flatMap?

map()

  • map lets you take the wrapped value
    and apply a function from type T to type U
    (T and U doesn't need to be different)
  • That function can return anything!
  • What if it returns Monad<U>?
    As we have seen map() would always
    wrap the returned value in a Monad,
    so if the function returned a Monad
    you will end with Monad<Monad<U>>.
    That's not usually what you want!

flatMap()

  • flatMap() is a convenient method to deal with mapper functions that return already a Monad!
  • It would apply the mapper but since the returned value is a Monad, it would not wrap it!
  • If the API is correctly written, you can't call flatMap if the mapper doesn't return a Monad
  • However if you are going to use map, remember to check what's the returned value of the mapper and see if you should be using flatMap instead!
public class Identity<T> implements Monad<T> {
  private T value;
  private Identity(T value) { this.value = value; }

  public static <T> Identity<T> of(T value) {
    return new Identity<T>(value);
  }
  public <U> Identity<U> map(Function<T, U> mapper) {
    U mapperValue = mapper.apply(value);
    return Identity.of(mapperValue); // can't escape the Monad!
  }
  // mapper already returns an Identity
  public <U> Identity<U> flatMap(Function<T, Identity<U>> mapper) {
    return mapper.apply(value); // no need to wrap returned value
  }
  public T get() {
    return value;
  }
}

Let's look at two
"real" Monads

Optional

Optional represents
a possibly absent value

Something like a
collection of
0 or 1 element

If an operation returns
null, then it becomes
an empty Optional and
all further operations
will be safely ignored

Never return null
(as this'll make code dishonest)
better use Optional

String title = Optional.of(page)
                       .map(Page::getTitle)
                       .orElse(StringUtils.EMPTY);
                       
  • We make an Optional of a given page
  • getTitle may return null but since in this case map will return a Monad we don't need to explicitly manage null-safety
  • orElse returns the value, if there is one or returns the given argument otherwise

 

String subtitle = Optional.of(page)
                          .map(Page::getSubtitle)
                          .orElseGet(() -> page.getSummary());
                       
  • We know Page is not null
  • A page might not have a subtitle
  • A page requires a summary
  • We try to get subtitle
  • If it's null, then we get the summary
  • orElseGet will only call the function passed,
    if neccesary when the Optional is empty
Optional<Event> nextEvent = Optional.of(events)
                                    .filter(list -> list.size() > 0)
                                    .map(list -> list.get(0));
                       
  • Optional doesn't need to be used to treat nulls
  • It's useful in other situations when the absence of a value is signaled not by null but something else e.g. an empty list
Optional.of(allArticles)
        .filter(articles -> articles.size() > 0)
        .map(articles -> articles.get(0))
        .map(Article::from)
        .orElse(null);
                       
  • Optional plays nicely with legacy technologies
  • JSP doesn't knows about Optional so in the orElse() method we return null. However we never had the possibility of an NPE while using Optional's methods
Optional.of(page.getContentResource())
        .map(content -> content.getChild("likes"))
        .map(Resource::getValueMap)
        .map(likesProperties -> likesProperties.containsKey(id))
        .orElse(false);
                       
  • Optional plays nicely with primitive values
Optional.ofNullable(node)
        .filter(n -> n.hasProperty("foo"))
        .map(n -> n.getProperty("foo"))
        .map(Property::getString)
        .orElse(StringUtils.EMPTY);
                       

Use Optional.ofNullable if the value
you want to wrap could be null

properties.put("shortsummary", description);
properties.put("type", type);
properties.put("groupName", urlName);
Optional.ofNullable(tags)
        .ifPresent(t -> properties.put("tags", t);

We can avoid if (tags != null) and write
code that while not shorter is simpler

Arrays.stream(values)
      .filter(Tag::valid)
      .findFirst() // returns an Optional
      .isPresent();

Some Streams operations
return an Optional

Stream.of(attributes)
      .filter(attribute -> StringUtils.startsWith(attribute, UID_KEY))
      .findFirst()
      .map(attribute -> attribute.replace(UID_KEY, StringUtils.EMPTY))
      .orElse(supervisorId);

So you can move from
Streams to Optional naturally

  • Never Use Null

  • Use Optional to eliminate null checks and improve code readability

Try

Try represents a value
or an exception that was thrown calculating it

Why do we
have Exceptions?

It's important to separate the code where an error happened and the code where the error is handled

Common Case: An error that happened in the persistent layer needs
to be transported to
the presentation layer

"The username / password you use don't match
our records"

What was used before?

"Special" return values, but these have problems:

1. If you need to
go across a lot of
methods, the returned
types may change so
you need to go from
one special value to other

2. There are methods
that may not have
a "special" value

3. Method signatures
are not "honest" you
need to remember
to check for the
"special" value

Then came
Global Variables,
e.g. ERRNO

But Global Variables are evil

Exceptions are great because there is an
"easy" method to
transport the error

But signature is still dishonest so you
may forget to
code around exceptions

Checked Exceptions seemed great

Until people started to
use them for control flow

// code

try {

} catch (SomeException e) {

}

// code

try {

} catch (OtherException e) {

}
// code

try {
   // code
   try {

   } catch (OtherException e) {

   }
   // code
} catch (SomeException e) {

}

// code
// code

try {
   
} catch (SomeException e) {
   // code
   try {

   } catch (OtherException e) {

   }
   // code
}

// code

Try<T> to the rescue!

Java doesn't have a Try monad yet, so we are
going to use vavr's

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.9.0</version>
</dependency>
Try.of(() -> node.getPath())
   .getOrElse(StringUtils.EMPTY);
  • If you have to deal with bad API's that return exceptions when an alternative was better, you can use Try
    as a kind of Optional
Try.of(() -> sourceNode.getPath())
   .map(Colors::getColorByPath)
   .getOrElse(EMPTY);
  • Same as with Optional,
    you can use map
Try.of(() -> Node.class.cast(source))
   .mapTry(parent -> parent.getNode("content"))
   .map(FileModel::hasMetadata)
   .getOrElse(false);
  • Use mapTry if that operation can also throws an exception
Try.of(() -> node.getPath())
   .getOrElseThrow(e -> new AssertionError(e));
  • You could avoid default values
  • This is also useful if you are
    refactoring existing code
  • Just remember that from a
    method you could return a
    Try<T> to propagate an error
Try.of(() -> node.getPath())
   .onFailure(e -> LOGGER.error(e))
   .getOrElse(StringUtils.EMPTY);
  • Or Log the error
Try.run(() -> {
  response.setStatus(HttpStatus.SC_OK);
  response.getWriter().write("Foo");
}).onFailure(e -> LOGGER.error("Can't send message to client", e));
  • You can use Try.run for operations that
    don't return a value

Remember:
Lambda Expression should be glue code!

public void doHead(HttpServletRequest request, HttpServletResponse response) {
    //retrieve the requested URL
    String uri = request.getParameter("uri");
    //obtain the session from the request
    Try.of(() -> request.getResourceResolver().adaptTo(Session.class))
       .andThen(session -> Try.run(() -> {
          session.checkPermission(uri, Session.ACTION_READ);
          LOGGER.debug("authchecker says OK");
          response.setStatus(SlingHttpServletResponse.SC_OK);
       })
                              .onFailure(e -> {
                                 LOGGER.error("authchecker says READ access DENIED!");
                                 response.setStatus(SlingHttpServletResponse.SC_FORBIDDEN);
       })
       .onFailure(e -> LOGGER.error("authchecker servlet exception: {}", e.getMessage())));
}

Instead of this...

Try.of(() -> request.getSession())
   .andThen(session -> Try.run(() -> checkPermission(uri, session, response))
                          .onFailure(e -> setForbiddenStatus(response))
   .onFailure(e -> LOGGER.error(e));

Write it like this...

private void checkPermission(String uri, Session session, 
      SlingHttpServletResponse response) throws RepositoryException {
  session.checkPermission(uri, Session.ACTION_READ);
  LOGGER.debug("authchecker says OK");
  response.setStatus(SlingHttpServletResponse.SC_OK);
}

private void setForbiddenStatus(SlingHttpServletResponse response) {
  LOGGER.error("authchecker says READ access DENIED!");
  response.setStatus(SlingHttpServletResponse.SC_FORBIDDEN);
}

These are the functions called from the lambdas

You can combine Monads!

Optional.of(node)
        .filter(myNode -> 
             Try.of(() -> myNode.hasNode("content"))
                .getOrElse(false))
        .map(myNode -> 
             Try.of(() -> myNode.getNode("content"))
                .getOrElse((Node) null));

Since lambdas can't throw exceptions you can use Try to mitigate this problem

  • Use Try to eliminate Try/Catch boilerplate

  • There are a lot more
    you can do with Try<T>
    so play with it!

But Try is not always a better alternative to Checked Exceptions

public void foo() 
      throws FileNotFoundException, XMLParsingException {
} 

I know exactly what exceptions will be thrown
and I can't ignore them
unless I really try to do it;
this reduces the chance
of bad exception handling

public Try<Void> foo() {}

I don't know how many exceptions can happen and nor of what type. I can silently ignore to handle them and
the compiler won't complain

There's also another Monad-ish type for Error Handling: Either<L, R>

Either is a Union Type:
is it an L or a R but never none and never both

For error handling, is assumed that the L will
be an exception and the expected value is R

At least now we can express one of the exceptions that
can happen...

... but compiler won't complain if you don't handle it and since you need to choose one,
you'll might end using Either<T, Exception>
which is worse
than the alternatives

The best will be a Monadic type with signature

Try<T, E... extends Exception> and
support from the Compiler

Java will require quite some changes to achieve it, but we can dream, right?

A little bit of math

Category Theory
studies sets and arrows

You can think of sets
as types and arrows
as functions

f : int → int
means that f is a
function that takes an
int an returns an int

One thing that is a cornerstone of
Mathematics is composability

So if
f : a → b
g : b → c
then I can create
a function
g ∘ f : a → c

Why composability is such an important concept?

Composability is the
best way to deal
with complexity

From simple and
tested functions, you
can compose them and create a more "complex" function which doesn't need to be tested

public long calculateDebt(List<User> users) {
   return users.stream()
               .filter(user -> user.type == PREMIUM)
               .sort(Compator.comparingLong(User::debt))
               .limit(50)
               .sum();
}

What would you test?

public List<Event> upcoming(final List<Node> nodes) {
  LocalDate now = LocalDate.now();
  return nodes.stream()
      .sorted(COMPARE_BY_EVENT_DATE)
      .map(Event::from)
      .filter(Event::isConfigured)
      .filter(event -> event.getDate().compareTo(now) >= 0)
      .collect(Collectors.toList());
}

If COMPARE_BY_EVENT_DATE,
Event::from and Event::isConfigured have
tests, what else would you test?

With filter, sorted, map, reduce, etc. I have
endless possibilities
to build complex
functions that don't need much more testing

What happens if
f : a → Mb
g : b → c
where M is extra data,
How can I return
to composability?

Monads to rescue!

With Monads I can have
f : a → Mb
g : b → Mc

h : Mc → c
So I can retain compatibility with code that's part of the Monad

There are 3 laws that ensure composability

Left Identity:
unit(x) bind f == f(x)
"If I create a Monad for x and then apply f, the value should be the same as if
I apply f directly to x"

Right Identity:
x bind unit == x
"If I have a Monad of x and I apply unit, I still have a Monad of x; unit is a zero-operation for Monads"

Associativity:
m.map(f).map(g) ==
m.map(g(f(x)))
"If I wrap x in a Monad and apply f and then g it's same as if I apply f and then g on x and then I wrap it in a Monad"

So Monads were introduced, to
retain composability,
with the ability to carry extra data (like IO)

You can design a lot of classes that take a Monadic approach even if you don't understand or strictly adhere to those laws!

Java 8's Optional doesn't strictly adhere to associativy rules and it's still awesome to use :)

Adhering to Mathematics can get in the way of getting the job done.
So yeah, Java 8's Optional isn't a Monad, it's really Monad-ish

No, it's not broken! is not just what you want it to be

Java's Optional is awesome for what it does in a
code-level: A container object which may or
may not contain
a non-null value.
In the documentation it never says it's a Monad!

Why's not a Monad?

Remember that chaining functions and using
a Monad in any place
in the chain should not change the results
if I never used a Monad

public final class Foo {
   public static Foo bar() {
      return null; // always return null
   }
   public static String neverDoThis(Foo foo) {
      if (foo == null) { // handle null, that's wrong!
         return "";
      }
      return "!!!";
   }
}
public final class Main {
  public static void main(String[] args) {
    // traditional composition
    Integer value = Foo.dontDoThis(Foo.bar());
    // first return null, then handles 
    // null as a special case!

    // Monadic composition
    Optional<Integer> monad = Optional.ofNullable(Foo.bar()) 
                                      // Optional is empty
                                      .map(Foo::dontDoThis); 
                                      // This is ignored

    System.out.println(monad.isPresent()); 
    // false after ofNullable it got empty
    System.out.println(value != null); 
    // true! is not "empty" without the Monad
  }
}

So Java's Optional breaks the 3rd law if-and-only-if we use a function
that handles null
as a special case

But that's stupid! Why would you ask for a Foo if you may never use it!

Instead, you should have two versions of you code, one that uses a Foo and one that doesn't

Java's Optional will not work with your stupid code, so instead of complaining that is broken, fix your API!

Good to know: Haskell is the FP language for excellence in the world! Monads are first-class citizens so there is no distinction between
T and Monad<T>

Learn to Love the Monad and the Monad-ish! They make you code more readable and less error prone + they look cool!

Boundaries

What to use?

  • Java's Optional
  • Guava's Optional
  • Vavr's Option
  • Vavr's Try
  • Attlasian's Fuse

None, do your own!

(kinda of)

What's the better implementation?

  • There are a lot of options!
  • It's hard to decide which is better
  • A newer and better option could be around the corner!
  • How do you guard yourself from changing implementations?
package code.cobregon.util;

import code.acme.util.Optional; // my current choice

public class Optional<T> {

   private code.cobregon.util.Optional wrapped;

   private Optional(T value) {
      wrapped = code.cobregon.util.Optional.of(value);
   }

   public static Optional<T> of(T value) {
      return new Optional(value);
   }

   public <U> Optional<U> map(Function<T, U> mapper) {
      return wrapped.map(mapper);
   }
   // all methods you want to expose; they should use delegation
}

Wrapped Optional

  • Changing the implementation
    is really easy
  • There's only ONE place in
    your code to do it
  • You can use the wrapper as a façade; so only implement the methods you are using, add more as you need them

Boundaries

  • Every time you are going to heavily use
    a third-party library, create a wrapper
    for it and use it across your code
  • Ig you want to change the
    implementation latter, it's painlessly
  • You can create the wrapper's API
    according to your needs
  • Use it for Optional, Try, StringUtils, etc.

Q&A

Thanks!

@gaijinco

Monads for Programmers

By Carlos Obregón

Monads for Programmers

  • 2,258