Fetzen aus Java & Vavr

»Vavr, Stream.unfold and type inference

I came across Stream.unfold. It seems to be a good solution if you need to read paginated results from a service or database. You can pass the current page or result as parameter to the function that is called in the unfold expression.

Only, Java runs into a type constraint issue for functions that are not inlined lambdas. You’ll get something like: “Incompatible equality constraint” when trying to use a function reference in Stream.unfold, Stream.unfoldRight or Stream.unfoldLeft.

It took me some time to figure out that it boils down to (a valid) type constraint in the Java compiler. To actually get the desired result, the function signature needs to be adjusted. Let’s assume something like this:

FooBarRepository repo = new FooBarRepository();

List<String> result = Stream
        .unfoldRight(1, repo::generateChunk)
        .flatMap(List::toStream).take(50).toList();
result.forEach(e -> log.info("element: {}", e));

Instead of declaring the generateChunk function like this:

public Option<Tuple2<List<String>, Integer>> generateChunk(int counter) {
  //...
}

adapt the signature to:

public Option<Tuple2<? extends List<String>, ? extends Integer>> generateChunk(int counter) {
  //...
}
»Tools for Java development
»Short circuit vavr Stream

If you are in a Stream, have to deal with Exceptions and still want to see which elements were processed successfully, short circuiting makes sense.

Stream<S3ObjectSummary> files = s3Service.listFiles();

files
  .map(S3ObjectSummary::getKey)
  .map(this::readFile)
  //short circuit stream if we have an error during reading
  .takeUntil(Try::isFailure).flatMap(t -> t)
  .map(this::handleFile)
  //short circuit stream if we have an error during handling
  .takeUntil(Try::isFailure).flatMap(t -> t)
  .map(this::cleanFile)
  //short circuit stream if we have an error during cleaning
  .takeUntil(Try::isFailure).flatMap(t -> t);
}
»Wrap exceptions in vavr Try

In current vavr (0.10.3), I really miss a generic mapFailure that I can use to wrap an earlier caught exception in a new exception. I came up with this helper funcion:

public class TryUtil {
  public static <T, R> API.Match.Case<T, R> wrap(Function<? super T, ? extends R> f) {
    Objects.requireNonNull(f, "f is null");

    return API.Case(API.$(), f);
  }
}

It can be used like this:

return Try.of(() -> s3Service.getObjectAsString(key))
        .mapFailure(wrap(ex -> new ProcessingException(key, ex)));
»Useful dependencies

mvn-repository

  • io.vavr » vavr
  • org.junit.jupiter » junit-jupiter-api
  • org.assertj » assertj-core
  • com.typesafe » config
  • org.projectlombok » lombok
  • org.slf4j » slf4j-api
  • ch.qos.logback » logback-classic
  • org.mockito » mockito-all
  • com.fasterxml.jackson.core » jackson-databind
  • com.google.inject » guice