javafunctional-programmingvavr

Combining Either's in Vavr?


I have a couple of Vavr Either's and I want to invoke a function with the Right value for each of these Either's. For example:

Either<MyError, String> either1 = ..
Either<MyError, String> either2 = ..
Either<MyError, String> either3 = ..

Either<MyError, String>> methodRequiringAllInputs(String, String, String) { 
..
}

I could of course do something like this:

either1.flatMap { value1 ->
    either2.flatMap { value2 ->
        either3.flatMap { value3 ->
            methodRequiringAllInputs(value1, value2, value3);
        }
    }
}

But this is very ugly. In other languages you could just use something like do-notation or for comprehensions to flatten out the structure. I know that Vavr has the concept of a Validation which is an applicative functor that allows you to do:

Validation<MyError, String> validation1 = ..
Validation<MyError, String> validation2 = ..
Validation<MyError, String> validation3 = ..

Validation.combine(validation1, validation2, validation3)
          .ap((validationValue1,validationValue2,validationValue3) -> .. );  

which is much nicer.

My question is if something similar exists in Vavr for Either's to avoid the nested flatMap structure? Note that I don't want to convert the Either's to Validation's.


Solution

  • There's a for comprehension construct in vavr that you could use for your use case. It helps you transform multiple Iterable, Option, Try, Future or List instances to another Iterator, Option, Try, Future or List instance respectively, by combining them (as rows of their cartesian product) into result values.

    In your case, Either being an Iterable on the right value, you can use the For construct for Iterables to construct a Tuple3 of the String right values, and iterate over the resulting Iterator by invoking your side-effecting code, or mapping/transforming them in whatever way you want. You will have a rich vavr Iterator, so that's a lot more flexible than a simple JDK Iterator.

    import static io.vavr.API.For;
    
    For(either1, either2, either3)
        .yield(Tuple::of)
        .forEach(t -> methodRequiringAllInputs(t._1, t._2, t._3));
    

    One small note though: in the above case, the result from the yield is a lazily evaluated Iterator. That means you'll need to iterate over it in the end in order to have the effects executed, so the forEach part is essential. You cannot move the side-effecting code into the yield part and skip the forEach, as the yield part will only be (lazily) executed when the resulting Iterator is being iterated over.