0

Is there an api for combining Try instances in Vavr that is similar to the way the Scalaz applicative operator |@| works?

Specifically, if I have more than two Try instances, for example, Try<X>, Try<Y> and Try<Z>, I would like to combine these instances in an applicative fashion using a 3-arg function.

I'm looking for a function signature that is similar to:

static <X, Y, Z, R> Try<R> combine(Try<X> x, Try<Y> y, Try<Z> z, Function3<X,Y,Z,R> func
divesh premdeep
  • 1,070
  • 3
  • 16
  • 28

2 Answers2

3

As far as I can see it doesn't support that usage directly. You can, however, achieve it using flatMap:

static <X, Y, Z, R> Try<R> combine(Try<X> tx, Try<Y> ty, Try<Z> tz, Function3<X,Y,Z,R> func) {
    return tx.flatMap(x -> ty.flatMap(y -> tz.map(z -> func.apply(x, y, z))));
}

If each Try value contains the same type then you can use a sequence operation:

public static void main(String[] args) {
    List<Try<String>> lt = List.of(Try.success("A"), Try.success("B"), Try.success("C"));
    Try<List<String>> tl = sequence(lt);
    System.out.println(tl);
}

static <T> Try<List<T>> sequence(List<Try<T>> lt) {
    return lt.foldRight(
        Try.success(List.empty()),
        (tt, tl) -> tt.flatMap(t -> tl.flatMap(l -> Try.success(l.prepend(t))))
    );
}

If you compare the input and output types you can see this essentially swaps the position of the Try and List containers. It's fairly idiomatic for monads, though typically you would implement it with applicative map operations instead of flatMap.

Alternatively, use Validation, which is designed to be used in an applicative style (via Validation.combine).

jon hanson
  • 8,722
  • 2
  • 37
  • 61
  • Yeah, although for more than 3 `Try` instances, those nested `flatMap` calls can get a bit hairy! I could use `Validation`, but it doesn't seem to be a good fit for my use case (except for the applicative syntax it supports). Thanks. – divesh premdeep Nov 15 '18 at 00:09
  • Or maybe I could write a convenience method that does exactly what you've suggested :thinking-face: – divesh premdeep Nov 15 '18 at 00:50
  • @diveshpremdeep I've edited the answer to offer another alternative - `sequence`. – jon hanson Nov 15 '18 at 07:50
0

As you mention there is no method like that on vavr, however you could made some workarounds:

using tupples:

    Try.success(authorizationHeader)
  .map(mapper::hcpFrom)
  .map(hcp -> Tuple.of(hcp, mapper.toSaveNoteRequest(noteId, patientId, requestBody, clock)))
  .map(t2 -> saveNoteUseCase.execute(t2._1(), t2._2()))
  .map(mapper::toHcpNoteResponse)
  .map(response -> accepted().body(response))
  .get();

using For with yield:

For(Try.success(authorizationHeader).map(mapper::hcpFrom), Try.success(mapper.toSaveNoteRequest(noteId, patientId, requestBody, clock)))
  .yield(saveNoteUseCase::execute)
  .map(mapper::toHcpNoteResponse)
  .map(response -> accepted().body(response))
  .get();

using code inside flatMap:

Try.success(authorizationHeader)
  .map(mapper::hcpFrom)
  .flatMap(hcp-> Try.of(() -> mapper.toSaveNoteRequest(noteId, patientId, requestBody, clock)).map(saveNoteRequest -> saveNoteUseCase.execute(hcp, saveNoteRequest)))
  .map(mapper::toHcpNoteResponse)
  .map(response -> accepted().body(response))
  .get();

And finally using futures(like api, however not recommended)

Try.success(authorizationHeader).map(mapper::hcpFrom).toCompletableFuture()
  .thenCombine(Try.success(mapper.toSaveNoteRequest(noteId, patientId, requestBody, clock)).toCompletableFuture(), saveNoteUseCase::execute)
  .thenApply(mapper::toHcpNoteResponse)
  .thenApply(response -> accepted().body(response))
  .join();

Kind regards.

nekperu15739
  • 3,311
  • 2
  • 26
  • 25