DEV Community

Cover image for Digest of Papers:  Monads For Functional Programming
Gustavo Preciado
Gustavo Preciado

Posted on

Digest of Papers: Monads For Functional Programming

Wadler, Philip , 2001
Read this post in spanish

Monads are one of the many concepts shared between mathematics and functional programming. It comes from categories theory to solve problems like exceptions control , counters or execution traces. This problems are easy to attack in imperatives languages but in the pure ones it can be a little complicated.

Pure functional languages have this advantage: all flow of data is made explicit. And this disadvantage: sometimes it is painfully explicit.

The paper explain how this explicit data flow improve the modularity and the importance of this feature in programming.

The paper explain how this explicit data flow improve the modularity and the importance of this feature in programming.

A monad, also called standard construction, is a data structure that encapsulate behaviors. This behaviors are commonly implements as side effects in imperative programming. A key example to understand monads is the Try value in Scala or VAVR(functional data structures for JAVA) that encapsulate exceptions. This value can be used to compose functions without explicit management of each possible error until the final apply.


public static Try<Integer> divide(Integer dividend, Integer divider) {
      return Try.of(() -> dividend/divider);
  }

  public static void main(String[] args) {
      Try<Integer> result = divide(100, 0).flatMap(r1 -> divide(r1, 10));
      System.out.println("RESULT 1 = " + resultado.map(String::valueOf)
       .getOrElseGet(Throwable::getMessage));
       // RESULT 1 = / by zero

      result = divide(100, 10).flatMap(r1 -> divide(r1, 10));
      System.out.println("RESULT 2 = " + resultado.map(String::valueOf)
       .getOrElseGet(Throwable::getMessage));
       // RESULT 2 = 1

  }

Monads Laws

The formal definition of monad include 3 laws to determine if the structure is a monad. This laws maybe have more theoric than practical value, but this blog is about the paper :P so I will try to explain the principles.

Left Unit/Left Identity

This law said that apply a function to a value and apply it to monad with the same value inside should be equal, if you use the correct composition(map or flatMap according to the return of the function).


  public static void main(String[] args) {
      Integer hundred = 100;
      Try<Integer> tryHundred = Try.of(() -> hundred);
      Boolean r = tryHundred.flatMap(value -> divide(100, value))
      .equals(divide(100, hundred));
      // True
  }
  return a >>= f = f

Right Unit/Right Identity

Like the previous law if we have a function that receive a value and encapsulate the result in a monad the result of applies it to the value or composes it with another monad function should be the same.


  public static void main(String[] args) {
      Integer hundred = 100;
      Try<Integer> tryHundred = Try.of(() -> hundred);
      // success return the same value encapsulated in a Try but cannot catch exceptions
      Boolean r = tryHundred.flatMap(value -> Try.success(value))
      .equals(Try.success(hundred));// True
  }
  m >>= return = m

This examples in JAVA appear to have the same implementation, but the difference between both laws is more evident in Haskell. Like I said this laws are theoric and Haskell is pure functional and more close to mathematics.

Associativity

The associativity law said that we should be able to compose functions sequentially or calling one as the input of the other.


public class MonadTest {

  public static Try<Integer> divideByTen(Integer dividend) {
      return Try.of(() -> dividend/10);
  }

  public static Try<Integer> doubleVal(Integer n1) {
      return Try.of(() -> n1 * 2);
  }

  public static void main(String[] args) {

      Try<Integer> tryHundred = Try.of(() -> 100);
      Try<Integer> r1 = tryHundred.flatMap(MonadTest::divideByTen).flatMap(doubleVal::doubleVal);
      Try<Integer> r2 = tryHundred.flatMap(
                        hundred -> divideByTen(hundred).flatMap(MonadTest::doubleVal)
                        );
      Boolean r = r1.equals(r2);// True

  }

}
  (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)

Top comments (0)