The meaning of the Optional form (Monad)

I don't deal with the exact concept of Monad, but try to look at the form used by Java Optional from a Monad application perspective.

Monad has a variety of applications and Optional is one of them (Optional are also called maybe monad).

The terms for Optional are summarized as follows.

Parameterised type : Optional<T>

  • T : Type parameter
  • Optional : Wrapper Type

That is, in Optional<T>, the T type was wrapped by the optional context.

The optional object can be created through the of method, and the code is as follows.

public final class Optional<T> {

    private final T value;

    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

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

cf) The of method is called unit from the perspective of Monad.

Now let's do an operation that adds two Optional<Integer> objects.

private Optional<Integer> optionalAdd(Optional<Integer> o1, Optional<Integer> o2) {
    if (o1.isPresent() && o2.isPresent()) {
        return Optional.of(o1.get() + o2.get());
    }

    return Optional.empty();
}

The easiest way is to get and add a value through the get method if there is a value in the Optional object(checked by the isPresent method). However, This method requires us to control the optional context ourselves.

There's a way to add without using the get method.

private Optional<Integer> optionalAdd(Optional<Integer> o1, Optional<Integer> o2) {
    return o1.flatMap(
            first -> o2.flatMap(
            second -> Optional.of(first + second)
            )
        );
}

If we make it like above, we don't have to deal with the optional context ourselves. cf) From the perspective of Monad, the flatMap method is called bind.

However, if you are not familiar with flatMap, it is difficult to understand the above code right away. This is even more so when flatMap is in the flatMap.

Now, let's understand flatMap more by creating a class similar to Optional. I create a wrap class and created a structure like Optional.

class Wrap<T> {
    private final T value;

    private Wrap(T value) {
        this.value = value;
    }

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

As shown below code, Wrap objects are created through the of method, and the value received as argument is filled with value.

Wrap<Integer> w1 = Wrap.of(1);
Wrap<Integer> w2 = Wrap.of(2);

Next, let's create a map method. The function is received as a parameter, and the result of performing the function is returned to the Wrap object.

public <R> Wrap<R> map(Function<T,R> mapper) {
    return Wrap.of(mapper.apply(value));
}

If you look at the code below, it passes the function when calling the map method to the w1 object containing 1. At this time, i enters 1 of w1 and the result is 10. The chaining performs the map again, and on the same principle, the final result is 110.

Wrap<Integer> w1 = Wrap.of(1);
Wrap<Integer> w2 = w1.map(i -> i + 9).map(i -> i * 11); // 110

However, if we create a function as shown below, the result becomes the Wrap type. It's a natural result to follow one by one, but we don't want this form.

Wrap<Integer> w1 = Wrap.of(1);
Wrap<Wrap<Integer>> map = w1.map(i -> Wrap.of(i + 1));

To solve this problem, a flatMap method was created.

public <R> Wrap<R> flatMap(Function<T, Wrap<R>> mapper) {
    return mapper.apply(value);
}

Two things have changed. One was changed from Function to Function and the other was removed from Wrap.of when returning.

The result was Wrap as desired.

Wrap<Integer> w1 = Wrap.of(1);
Wrap<Integer> flatMap = w1.flatMap(i -> Wrap.of(i + 1));

Let's look at the code that adds the two optional again.

private Optional<Integer> optionalAdd(Optional<Integer> o1, Optional<Integer> o2) {
    return o1.flatMap(
            first -> o2.flatMap(
            second -> Optional.of(first + second)
            )
        );
}

fisrt, second variables are the Integer type. It's easier to understand If I changed the code a little more.

private Optional<Integer> optionalAdd(Optional<Integer> o1, Optional<Integer> o2) {
  return o1.flatMap(
    first -> {
      Optional<Integer> outer = o2.flatMap(
        second -> {
          Optional<Integer> inner = Optional.of(first + second);
          return inner;
        }
      );
      return outer;
    }
  );
}

If we use a map other than flatMap, the return type is Optional<Optional<Optional<Integer>>>.

reference : youtube.com/watch?v=vePeILeSv4E
reference : nazarii.bardiuk.com/posts/java-monad.html
reference : medium.com/@afcastano/monads-for-java-devel..
reference : medium.com/@afcastano/monads-for-java-devel..