11

Exploring the new features of Java 8, I stumbled the wish to create a Consumer<X> by chaining a Consumer<Y> to Function<X,Y>.

Does this make sense? And if so, how would a good (general) solution look like?


What I've tried (rather a special case by example):

Given

@FunctionalInterface
public interface PartialFunction<X, Y> {
    Y apply(X x) throws Exception;
}

and

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

public class PartialFunctions {

    public static <X, Y> Function<X, Optional<Y>> withOptionalResults(final PartialFunction<X, Y> funcThatThrows) {
        return z -> {
            try {
                return Optional.of(funcThatThrows.apply(z));
            } catch (final Exception e) {
                return Optional.empty();
            }
        };
    }

    public static <X, Y> Consumer<X> acceptOnSuccess(final PartialFunction<X, Y> g, final Consumer<Y> c) {
        return x -> withOptionalResults(x).apply(t).ifPresent(c);
    }
}

I end up with a possible usage like:

files.forEach(PartialFunctions.<File, BufferedImage>acceptOnSuccess(
        ImageIO::read, images::add));

However, the need for the explicit generic specification is not optimal. Hopefully there is something better?

Jens Piegsa
  • 7,399
  • 5
  • 58
  • 106
  • 1
    I think this is fine as it is. Apart from a typo, it compiles under b106 without the type witness to the call of `acceptOnSuccess`. I much prefer using `Optional`, as here, to `null`. – Maurice Naftalin Dec 14 '13 at 08:01

4 Answers4

10
interface IgnoreThrowing<F,V> extends Function<F,V> {
    public default V apply(F from) {
        try {
            return ignore(from);
        } catch(Exception e) {
            return null;
        }
    }
    public V ignore(F from) throws Exception;
}

class Throwables {
    public static <F,V> Function<F,V> ignore(IgnoreThrowing<F,V> f) {
        return f;
    }
}

static {
    files.map(Throwables.ignore(ImageIO::read)).collect(...)
}

It will get better if you add a Collector that ignores nulls as input.

edit: i wrote this without syntax checking or compiling, so not totally sure about the placement of the default, and whether the compiler can successfully infer the chained function type parameters.

Maurice Naftalin
  • 10,354
  • 2
  • 24
  • 15
aepurniet
  • 1,719
  • 16
  • 24
  • Thanks for the hint to [`defaults`](http://stackoverflow.com/q/16764791/1725096)! – Jens Piegsa Dec 13 '13 at 17:47
  • Cool stuff... The way you did `Throwables::Ignore` is very useful way beyond just this answer/example. – Alex Pakka Dec 13 '13 at 18:20
  • `Throwables::Ignore(..)` should be `Throwables.Ignore(..)` – aepurniet Dec 13 '13 at 19:31
  • I had some issues with `IgnoreThrowing` being detected as a functional interface, but this seems yet to be a problem of the compiler of my IDE. – Jens Piegsa Dec 14 '13 at 16:53
  • 3
    -1 Don't swallow the exception. Don't make a naming conflict with Google's Guava. Name your classes better. ("IgnoreThrowing" is a verb, while classes should be nouns like "PropagatingFunctionDecorator"). And finally, explain your technique *before* you throw a bunch of code on the page. – Aleksandr Dubinsky Dec 16 '13 at 15:26
  • its just supposed to show people an approach, not meant to be used in a nuclear reactor... – aepurniet Dec 16 '13 at 19:14
5

You could extend the Function interface like so:

public interface ComposableFunction<T, R> extends Function<T, R> {

    default Consumer<T> andThen(Consumer<R> after) {
        Objects.requireNonNull(after);
        return (T t) -> {after.accept(apply(t));};
    }

}

And then use it regularly like so:

ComposableFunction<Throwable, String> getMessage = Throwable::getMessage;
Consumer<String> log = System.out::println;
Consumer<Throwable> logMessage = getMessage.andThen(log);
david-hoze
  • 1,045
  • 15
  • 31
2

You could try something like this?

public void lambdaChaining() {
    System.out.println("\nlambda chaining:");
    List<String> l = Arrays.asList("1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", "999999999");
    Function<String, ?> f = s -> s.length();
    Consumer<String> c = s -> System.out.println(f.apply(s));;
    l.forEach(c);
}
bungee
  • 61
  • 5
0

the other option (and the more general case) would be to use new Function<X,Y>(...).andThen(new Function<Y,Z>(...)). I wouldnt mix consumers and functions, rather chain functions then feed into consumers (who optionally are also chained).

aepurniet
  • 1,719
  • 16
  • 24