FnChain.java
package no.motif.f.combine;
import no.motif.NOP;
import no.motif.f.Do;
import no.motif.f.Fn;
/**
* Composition of multiple {@link Fn Fns}, where the result of the
* first is passed as argument to the second.
* <pre>
* g(ƒ(x))
* </pre>
*/
public abstract class FnChain<I, O> implements Fn<I, O> {
public static <I, O> FnChain<I, O> chain(Fn<I, O> first) {
return new Impl<>(first, NOP.<O>fn(), null);
}
/**
* This chain, typed as a {@link Do} (sideeffect).
*/
@SuppressWarnings("unchecked")
public final Do<I> asDo = (Do<I>) this;
/**
* Append a function to the chain.
*
* @param next The next function.
* @return the resulting new chain with the function appended.
*/
public abstract <Y> FnChain<I, Y> then(Fn<? super O, Y> next);
/**
* Append a {@link Do} (sideeffect) on the output from the last {@link Fn} in the chain.
* The chain may still be chained with further <code>Fn</code>s or <code>Do</code>s. The chain
* yields the result from the last <code>Fn</code> as usual, after it has been passed to the given
* <code>Do</code>. If you need the resulting chain as a <code>Do</code>, use {@link #asDo}.
*
* @param sideeffect The {@link Do} to apply on the output from the last <code>Fn</code> in the chain.
* @return The resulting new chain.
*/
public abstract FnChain<I, O> then(Do<? super O> sideeffect);
private FnChain() { }
private static class Impl<I, T, O> extends FnChain<I, O> implements Do<I> {
private final Fn<I, T> first;
private final Fn<? super T, O> second;
private final Do<? super O> sideeffect;
private Impl(Fn<I, T> first, Fn<? super T, O> second, Do<? super O> sideeffect) {
this.first = first;
this.second = second;
this.sideeffect = sideeffect;
}
@Override
public <N> FnChain<I, N> then(Fn<? super O, N> next) {
return new Impl<>(this, next, null);
}
@Override
public FnChain<I, O> then(final Do<? super O> action) {
return new Impl<>(first, second, action);
}
@Override
public O $(I value) {
O result = second.$(first.$(value));
if (sideeffect != null) sideeffect.with(result);
return result;
}
@Override
public void with(I value) {
$(value);
}
}
}