понедельник, 7 ноября 2016 г.

Знакомство с Optional монадой в Java 8

Когда я слышал слово "монада", то представлял себе экзотические языки программирования как Haskell, Scala. После встретил, очередной раз погружаясь в изучение Javascript. Ну а как же Java?

Теперь и в Java можно найти монаду, приложив усилие.

Те, кому повезло работать на Java 8, наверняка встречались с Optional. Из официальной документации мы видим, что Optional это:

A container object which may or may not contain a non-null value.

Как мы опишем монаду? Matt Fowler описывает ее так:

Представим монаду как контейнер, который оборачивает значение и предоставляет нам набор трансформаций над этим значением и возможность получить это значение обратно со всеми примененными трансформациями
Мы кладем значение в т.н. монадический контекст, применяем трансформации и получаем конечное значение обратно.

Когда мы говорим о трансформации, имеем в виду:


<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)// если значение представлено,
//применяем к нему маппер и возвращаем Optinal с этим резудьтатом, если не представлено - пустой Optional

Пока сходится. Но зачем это нужно?

Представим себе объект с большой степенью вложенности Employee -> Address -> ZipCode -> (String) getLastFour. Чтобы добраться до нужного значение преодолеваем цепочку if:


public String getLastFour(Employee employee) {
    if(employee != null) {
        Address address = employee.getPrimaryAddress();
        if(address != null) {
            ZipCode zip = address.getZipCode();
            if(zip != null) {
                return zip.getLastFour()
            }
        }
    }
    throw new FMLException("Missing data");
}
Но чем нам может помочь монада? Она позволяет обернуть потенциально нулевой объект и применить к нему ряд трансформаций и получить конечное значение, если он, объект, не null. Optional позволяет нам это делать:

public String getLastFour(Optional employee) {
    return employee.flatMap(employee -> employee.getPrimaryAddress())
                   .flatMap(address -> address.getZipCode())
                   .flatMap(zip -> zip.getLastFour())
                   .orElseThrow(() -> new FMLException("Missing data"));
} 
orElseThrow позволяет вернуть значение, если представлено, если нет - бросить Exception

Достаточно ли этого чтобы считать Optional монадой? Нет. Монад должна удовлетворять 3 законам:

Left unit law - если мы обернем значение в монаду и свяжем монаду с функцией, то результат будет такой же, как после применения функции к значению unit(x) flatMap f == f(x):


Function<Integer, Optional<Integer>> addOne = x -> Optional.of(x + 1);
Optional.of(5).flatMap(addOne).equals(addOne.apply(5))

Right unit law - если мы свяжем монаду с функцией, которая создает монаду из данных, свяжем монаду с функцией, то получим ту же монаду monad flatMap unit == monad:


Function<Integer, Optional<Integer>> addOne = x -> Optional.of(x + 1);
Optional.of(5).flatMap(addOne).equals(addOne.apply(5))

Associativity law - если мы применяем к монаду ряд трансформаций, то их порядок не важен (monad flatMap f) flatMap g == monad flatMap(x => f(x) flatMap g) :



Function<Integer, Optional<Integer>> addOne = i -> Optional.of(i + 1);
Function<Integer, Optional<Integer>> addTwo = i -> Optional.of(i + 2);
Function<Integer, Optional<Integer>> addThree = i -> addOne.apply(i).flatMap(addTwo);
Optional.of(5).flatMap(addOne).flatMap(addTwo).equals(Optional.of(5).flatMap(addThree));

Вывод:

Мы можем классифицировать Optional как IO монаду (монада строго последовательных вычислений, стратегия связывания — "сначала первое вычисление, затем второе")

Материалы:
Understanding the Optional Monad in Java 8
Монады в Scala
Does JDK8's Optional class satisfy the Monad laws? Yes, it does.

Комментариев нет:

Отправить комментарий