Package com.github.sviperll.result4j


package com.github.sviperll.result4j
The package provides Result-type similar to Result-type in Rust that allows to return either successful result or otherwise some kind of error.

In Java, the native way of reporting errors are exceptions, either checked or unchecked. You do not need Result-type most of the time in Java-code, where you can directly throw exceptions. But there are situations, where more functional-style is used. In such situations pure-functions are expected that throw no exceptions. Handling exception in such situations can be cumbersome and require a lot of boilerplate code. Result-type and associated helper-classes (like Catcher) help with exception handling and allow to write idiomatic functional code that can interact with methods that throw exceptions.

Result-type provides a way to pass error information as a first-class value through the code written in functional style. Routines are provided for interoperability of normal code that uses exception and functional code that uses Result-type, so that exceptions can be caught and propagated as errors in Result-type and then rethrown again later in the control-flow.

    Catcher.ForFunctions<IOException> io =
        Catcher.of(IOException.class).forFunctions();
    String concatenation =
            Stream.of("a.txt", "b.txt", "c.txt")
                    .map(io.catching(name -> loadResource(name)))
                    .collect(ResultCollectors.toSingleResult(Collectors.join()))
                    .throwError(Function.identity());

Above code uses Catcher class to adapt functions that throw exceptions to return Result-type instead. ResultCollectors class contains helper-methods to collect multiple Results into a single one. You do not need Catcher class in normal Java-code, where you can directly throw exceptions. But the above snippet can serve as an example of situations, where more functional-style is used. In such situations pure-functions are expected that throw no exceptions. Handling exception in such situations can be cumbersome and require a lot of boilerplate code.

There is also an AdaptingCatcher class that allows to adapt or wrap exceptions.

    AdaptingCatcher.ForFunctions<IOException, PipelineException> io =
            Catcher.of(IOException.class).map(PipelineException::new).forFunctions();
    AdaptingCatcher.ForFunctions<MLException, PipelineException> ml =
            Catcher.of(MLException.class).map(PipelineException::new).forFunctions();
    List<Animal> animals1 =
            List.of("cat.jpg", "dog.jpg")
                    .stream()
                    .map(io.catching(Fakes::readFile))
                    .map(Result.flatMapping(ml.catching(Fakes::recognizeImage)))
                    .collect(ResultCollectors.toSingleResult(Collectors.toList()))
                    .throwError(Function.identity());
    Assertions.assertEquals(List.of(Animal.CAT, Animal.DOG), animals1);