Search code examples
javavavr

Handling exceptions idiomatically with VAVR


Having spat the Google Guava kool-aid back out of our mouths, and diving head-first into our new infatuation with VAVR and its sparkling ideals, say we are map()ping a Stream, performing foldLeft() on a Traversable, or similar, and one of the inner functions throws a checked exception. Now we are staring down at compiler errors, unhandled exceptions.

How aught such exceptions be handled ideally, with idiomatic VAVR. What does the resulting code look like, what are the patterns. We have Options and Trys... How does it all come together.

Oh, and tricks like sneaky exceptions disinterest us.


Solution

  • You can use CheckedFunction[0-8].liftTry() to convert a function that throws checked exceptions to a total function that returns the result of the original function wrapped in a Try. If the original function returns a value without throwing, it will be wrapped in a Success, if it throws, the exception will be wrapped in a Failure.

    You will then need to take a decision on how to handle errors in the context of multiple values. Here are some examples on what you could do with a bunch of Try values.

    Array<String> input = Array.of(
            "123", "456", "789", "not a number", "1111", "another non-number"
    );
    
    // try and parse all the strings
    Array<Try<Integer>> trys = input.map(CheckedFunction1.liftTry(Integer::parseInt));
    
    // you can take just the successful values
    Array<Integer> values = trys.flatMap(Try::iterator);
    
    // you can look just for the failures
    Array<Throwable> failures = trys.filter(Try::isFailure).map(Try::getCause);
    
    // you can partition by the outcome and extract values/errors
    Tuple2<Traversable<Integer>, Traversable<Throwable>> partition =
        trys.partition(Try::isSuccess)
            .map(
                seq -> seq.map(Try::get),
                seq -> seq.map(Try::getCause)
            );
    
    // you can do a short-circuiting parse of the original sequence
    // this will stop at the first error and return it as a failure
    // or take all success values and wrap them in a Seq wrapped in a Try
    Try<Seq<Integer>> shortCircuit = Try.sequence(
        input.iterator() //iterator is lazy, so it's not fully evaluated if not needed
             .map(CheckedFunction1.liftTry(Integer::parseInt))
    );
    // Failure(java.lang.NumberFormatException: For input string: "not a number")
    

    Of course, you can use any other vavr collection in place of Array.