1

I have a series of functions that take in a Request object and return a Vavr Either.

The Either will contain a Result object if the task is complete or a modified Request object if the task needs to be completed by another function.

The thought was that I could chain them together by doing something like this:

// Note: The Request object parameter is modified by the function 
// before being returned in the Either.
Function<Request, Either<Request,Result>> function1;
Function<Request, Either<Request,Result>> function2;
Function<Request, Either<Request,Result>> function3;
Function<Request, Result> terminalFunction;

Result result = function1.apply(request)
            .flatMapLeft(function2)
            .flatMapLeft(function3)
            .fold(terminalFunction, r->r);

But apparently flatMapLeft is not a thing, so I just end up with a nested Eithers on the left side. Any ideas on how I can achieve this functionality? I'm open to alternative libraries.

Edit:

Result result = function1.apply(request)
            .fold(function2, Either::right)
            .fold(function3, Either::right)
            .fold(terminalFunction, r->r);

Seems like this should work instead, but Intellij is giving this error on the second fold line:

no instance(s) of type variable(s) exist so that capture of ? extends Object conforms to Request
zkello
  • 239
  • 2
  • 15
  • You need to take the either in as well as out. – Thorbjørn Ravn Andersen May 07 '20 at 19:02
  • In the functions? I'm not sure I like that. The whole point here is to avoid running the additional functions unnecessarily. – zkello May 07 '20 at 19:23
  • flatMapLeft is not a thing, because `Either` is only a monad in `B`. –  May 07 '20 at 19:51
  • Sorry, but that is how streams work in Java. – Thorbjørn Ravn Andersen May 07 '20 at 20:53
  • I did some work on seeing if this exact usecase could be made feasible with streams. My experiment - StreamTuples - worked, but my conclusion was after using it in some non-trivial projects that the advantage is too little to overshadow the major obstacle that Java does not support tuples/pairs as return values. https://github.com/ravn/streamtuples – Thorbjørn Ravn Andersen May 07 '20 at 20:58
  • Who said anything about streams? The StreamTuples idea is interesting, but I don't think that works for this use case. The Request object passed into the function is not the same as the one returned in the Either. I think @bob is right, the issue here is that this Either is right biased. I need an unbiased Either. Interesting discussion on that here: https://stackoverflow.com/questions/12290858/why-is-scalas-either-not-a-monad – zkello May 08 '20 at 19:13
  • 1
    If you want `Either` to be unbiased, it cannot be a monad, because monad requires a type parameterized with a single type parameter (kind `* -> *`). When we use `Either` as a right-biased type it usually represents computations that may fail and the monad instance is used to sequence several of such effectful computations. If you don't need the error context you can use an unbiased `Either` along with the Bifoldable type class, its catamorphism, a functional prism or even bare pattern matching to process it. Unfortunately I don't know Java/Vavr... –  May 08 '20 at 20:40

2 Answers2

3

You need monadic composition on your Request side, which is left side in your type signatures, but you have monadic composition for Either on the right side. So you need to swap your eithers in your function definitions or you have to pass them through Either.swap() with

Function1.of(SomeType::function1).andThen(Either::swap)

Essentially, each of your function[1-3] would then become of type:

Function<Request, Either<Result, Request>>

Then your call chain becomes:

Result result = function1.apply(request)
        .flatMap(function2)
        .flatMap(function3)
        .swap()
        .getOrElseGet(terminalFunction);
Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
  • Interesting! I guess the downside of this approach is you end up breaking the "left equals failure, right equals success" convention, but it's probably a better use of the data structure as intended. – zkello May 08 '20 at 18:46
  • 1
    The people that created `Either` might have had something like that in mind, but not necessarily (I don't know about the history), and the resulting construct doesn't really care anymore. You could think of _left_ as an _early return_, or _short-circuiting_, and _right_ as something that is candidate for further processing, i.e. _bind_. If you forget _left_ being _error_ and think in those terms, everything start to make more sense. – Nándor Előd Fekete May 08 '20 at 20:52
0
Result result = function1.apply(request)
            .fold(function2, Either::<Request, Result>right)
            .fold(function3, Either::<Request, Result>right)
            .fold(terminalFunction, r->r);

This appears to work, although it's a little clunky. Is this an abuse of the library? Would be interested in hearing some alternative approaches.

zkello
  • 239
  • 2
  • 15