Mały lifting funkcji
Na początek małe wprowadzenie teoretyczne. Istnieją funkcje, których dziedzina jest w jakiś sposób ograniczona do pod zbioru obiektów należących do danego typu. Najlepszym przykładem jest dzielenie (dla uproszczenia liczb całkowitych), które działa o ile dzielnik nie jest zerem. Wynik takiej operacji jest nieokreślony, albo najzwyczajniej błędny. Jeżeli przenosimy taką funkcję do kodu to możemy:
- Stworzyć (pod)typ, który będzie zawierać tylko elementy należące do dziedziny.
- Wyrzucić wyjątek.
Pierwsze rozwiązanie jest czasami stosowane w niektórych językach. Wymaga jednak bardzo dobrze przemyślanego systemu typów, który dopuszcza m.in. wielodziedziczenie czy też niejawną konwersję argumentów. Drugie podejście jest znacznie prostsze i w pewnym sensie mniej w nim magii.
Co, jednak jeżeli chcemy, by funkcja nie waliła nam wyjątkami? Ewentualnie mamy na wejściu funkcję, jakąś, i nie wiemy, czy po jej wywołaniu z podanymi parametrami nie nastąpi wybuch?
Lifting funkcji
Dokładny opis działania mechanizmy znajdziecie na wikiW. Ja pokażę, jak działa lifting w Javaslang.
Listing 1. Bezpieczne dzielenie
public void exampleLifting() {
Function2<Integer, Integer, Integer> div = (a, b) -> a / b;
Function2<Integer, Integer, Option<Integer>> saveDiv = Function2.lift(div);
System.out.println(saveDiv.apply(1, 0));
}
Nasza pierwotna funkcja div wywali się, gdy tylko b będzie równe 0. Jednak jeżeli użyjemy metody lift, by wynik opakować w Optional, to wywołanie z niepoprawnym parametrem zwróci None. Program się nie wywali.
Podsumowanie
Procedura liftingu, która jest dostępna w Javaslang, choć nie jest w pełni zgodna z matematyczną definicją, to stanowi bardzo dobre narzędzie zabezpieczające. Pozwala nie tyle, co na walidację argumentu wejściowego w postaci funkcji, ale na sanityzacjęW parametrów wejściowych, które są funkcjami. Dzięki temu mamy pewność, że funkcje, które otrzymujemy, nie popsują nam kodu w nieprzewidywalny sposób.
Wadą tego rozwiązania jest konieczność stosowania funkcji z API Javaslang, a nie funkcji z podstawowego API Javy 8. Choć zawsze można…
Listing 2. Implementacja lift dla BiFunction
public <T, U, R> BiFunction<T, U, Option<R>> lift(BiFunction<T, U, R> f) {
return (t, u) -> Try.of(()-> f.apply(t, u)).getOption();
}