Examples of vavr function lifting.
Reference: https://www.vavr.io/vavr-docs/#_lifting
A partial function from X
to Y
is a function f: X′ → Y
,
for some X′ c X
. For x e X\X′
function is undefined.
In programming, if partial function is called with a disallowed input value, it will typically throw an exception.
In programming - we lift function f
to f′: X -> Option<Y>
in such a manner:
f′(x).get() = f(x)
onX′
f′(x) = Option.none()
forx e X\X′
In vavr we have two approaches to lifting:
- lifting function with
Option
Function2<Integer, Integer, Integer> divide = (a, b) -> a / b; Function2<Integer, Integer, Option<Integer>> lifted = Function2.lift(divide);
- lifting function with
Try
Function2<Integer, Integer, Integer> divide = (a, b) -> a / b; Function2<Integer, Integer, Try<Integer>> lifted = Function2.liftTry(divide);
- suppose we have:
BlockedUser
with a method to activate it:@Value @Builder class BlockedUser { int id; int warn; LocalDate banDate; ActiveUser activate(Clock clock) { if (warn > 10) { throw new BusinessException("id = " + id + ": warns has to be <= 10"); } if (ChronoUnit.DAYS.between(banDate, LocalDate.now(clock)) < 14) { throw new BusinessException("id = " + id + "minimum ban time is 14 days!"); } return ActiveUser.builder().id(id).build(); } }
- simple
ActiveUser
@Value @Builder class ActiveUser { int id; }
- suppose we cannot modify class
BlockedUser
- we have
Stream
ofBlockedUsers
and we want to activate them (if possible) and save to the databaseactivate
throws exceptions, so welift
that function (FunctionLiftingTest - lift()
):// given ActiveUserRepository activeUserRepository = new ActiveUserRepository(); var cannotBeActive = BlockedUser.builder() .id(1) .banDate(LocalDate.parse("2014-10-12")) .warn(15) .build(); var canBeActive = BlockedUser.builder() .id(2) .banDate(LocalDate.parse("2016-10-12")) .warn(0) .build(); var now = Clock.fixed(Instant.parse("2016-12-03T10:15:30Z"), ZoneId.systemDefault()); // when Stream.of(cannotBeActive, canBeActive) .map(Function1.lift(x -> x.activate(now))) .forEach(option -> option.peek(activeUserRepository::add)); // then assertTrue(activeUserRepository.existsAll(List.of(2)));
- we have
Stream
ofBlockedUsers
and we want to activate them (if possible) and save to the database or generate report of exceptions occurred during activation (FunctionLiftingTest - liftTry()
)// given ActiveUserRepository activeUserRepository = new ActiveUserRepository(); var cannotBeActive = BlockedUser.builder() .id(1) .banDate(LocalDate.parse("2014-10-12")) .warn(15) .build(); var canBeActive = BlockedUser.builder() .id(2) .banDate(LocalDate.parse("2016-10-12")) .warn(0) .build(); var now = Clock.fixed(Instant.parse("2016-12-03T10:15:30Z"), ZoneId.systemDefault()); List<String> fails = new LinkedList<>(); // when Stream.of(cannotBeActive, canBeActive) .map(Function1.liftTry(x -> x.activate(now))) .forEach(tryF -> tryF .onSuccess(activeUserRepository::add) .onFailure(exception -> fails.add(exception.getMessage()))); // then assertThat(activeUserRepository.count(), is(1)); assertTrue(activeUserRepository.existsAll(List.of(2))); // and assertThat(fails, hasSize(1)); assertThat(fails.get(0), is("id = 1: warns has to be <= 10"));