CurriedFunction.java
package com.github.dakusui.pcond.experimentals.currying;
import com.github.dakusui.pcond.experimentals.currying.multi.MultiFunction;
import com.github.dakusui.pcond.internals.InternalUtils;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Function;
import static com.github.dakusui.pcond.experimentals.currying.Checks.isValidValueForType;
import static com.github.dakusui.pcond.experimentals.currying.Checks.validateArgumentType;
public interface CurriedFunction<T, R> extends Function<T, R> {
R applyFunction(T value);
default R apply(T value) {
return Checks.ensureReturnedValueType(this.applyFunction(validateArg(value)), returnType());
}
@SuppressWarnings("unchecked")
default <V> V applyLast(T value) {
return (V) Checks.requireLast(this).apply(value);
}
@SuppressWarnings("unchecked")
default <V extends CurriedFunction<T, R>> V applyNext(T value) {
return (V) requireHasNext(this).apply(value);
}
static <V extends CurriedFunction<T, R>, T, R> V requireHasNext(V value) {
if (!value.hasNext())
throw new NoSuchElementException();
return value;
}
Class<?> parameterType();
Class<?> returnType();
default boolean hasNext() {
return CurriedFunction.class.isAssignableFrom(returnType());
}
default boolean isValidArg(Object arg) {
return isValidValueForType(arg, this.parameterType());
}
default <V> V validateArg(V arg) {
return validateArgumentType(arg, parameterType(), CurryingUtils.messageInvalidTypeArgument(arg, parameterType()));
}
class Impl implements CurriedFunction<Object, Object> {
private final MultiFunction<Object> function;
private final List<? super Object> ongoingContext;
public Impl(MultiFunction<Object> function, List<? super Object> ongoingContext) {
this.function = function;
this.ongoingContext = ongoingContext;
}
@Override
public Class<?> parameterType() {
return function.parameterType(ongoingContext.size());
}
@Override
public Class<?> returnType() {
if (ongoingContext.size() == function.arity() - 1)
return function.returnType();
else
return CurriedFunction.class;
}
@Override
public Object applyFunction(Object p) {
if (ongoingContext.size() == function.arity() - 1)
return function.apply(InternalUtils.append(ongoingContext, p));
return CurryingUtils.curry(function, InternalUtils.append(ongoingContext, p));
}
}
}