io.rouz.task.TaskBuilders Maven / Gradle / Ivy
package io.rouz.task;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import io.rouz.task.TaskContext.Value;
import io.rouz.task.dsl.TaskBuilder;
import io.rouz.task.dsl.TaskBuilder.F0;
import io.rouz.task.dsl.TaskBuilder.F1;
import io.rouz.task.dsl.TaskBuilder.F2;
import io.rouz.task.dsl.TaskBuilder.F3;
import io.rouz.task.dsl.TaskBuilder.F4;
import io.rouz.task.dsl.TaskBuilder.TaskBuilder1;
import io.rouz.task.dsl.TaskBuilder.TaskBuilder2;
import io.rouz.task.dsl.TaskBuilder.TaskBuilder3;
import io.rouz.task.dsl.TaskBuilder.TaskBuilderC;
import io.rouz.task.dsl.TaskBuilder.TaskBuilderC0;
import io.rouz.task.dsl.TaskBuilder.TaskBuilderCV;
import io.rouz.task.dsl.TaskBuilder.TaskBuilderCV0;
import static java.util.stream.Collectors.toList;
/**
* Package local implementation of the {@link TaskBuilder} tree.
*
* These classes tackle the exponential growth of paths that can be taken through the
* {@link TaskBuilder}X interfaces by linearizing the implementation through composing functions.
*
* The linearization is implemented by letting the next builder in the chain take either a
* {@link RecursiveEval} or {@link ChainingEval}. This evaluator allows the builder to chain
* onto the evaluation by including more input tasks. The evaluator will finally be used to
* terminate the builder by enclosing a function into an {@link EvalClosure} for a {@link Task}.
*/
final class TaskBuilders {
static TaskBuilder rootBuilder(TaskId taskId, Class type) {
return new Builder0<>(taskId, type);
}
// #############################################################################################
private static class Builder0 extends BaseRefs implements TaskBuilder {
Builder0(TaskId taskId, Class type) {
super(taskId, type);
}
@Override
public Task process(F0 code) {
return Task.create(inputs, type, gated(taskId, code), taskId);
}
@Override
public Task processWithContext(F1> code) {
return Task.create(inputs, type, gatedVal(taskId, code), taskId);
}
@Override
public TaskBuilder1 in(F0> aTask) {
F0> aTaskSingleton = Singleton.create(aTask);
TaskId taskId = this.taskId; // local ref to drop ref to Builder0 instance
return new Builder1<>(
lazyFlatten(inputs, lazyList(aTaskSingleton)),
taskId, type,
leafEvalFn(tc -> {
Value aValue = tc.evaluate(aTaskSingleton.get());
return f1 -> aValue.flatMap(gated(taskId, tc, f1));
}),
leafEvalFn(tc -> {
Value aValue = tc.evaluate(aTaskSingleton.get());
return f1 -> aValue.flatMap(gatedVal(taskId, tc, f1));
}));
}
@Override
public TaskBuilder1, Z> ins(F0>> aTasks) {
F0>> aTasksSingleton = Singleton.create(aTasks);
TaskId taskId = this.taskId; // local ref to drop ref to Builder0 instance
return new Builder1<>(
lazyFlatten(inputs, lazyFlatten(aTasksSingleton)),
taskId, type,
leafEvalFn(tc -> {
Value> aListValue = aTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList());
return f1 -> aListValue.flatMap(gated(taskId, tc, f1));
}),
leafEvalFn(tc -> {
Value> aListValue = aTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList());
return f1 -> aListValue.flatMap(gatedVal(taskId, tc, f1));
}));
}
@Override
public TaskBuilderC0 curried() {
return new BuilderC0<>(taskId, type);
}
@Override
public TaskBuilderCV0 curriedWithContext() {
return new BuilderCV0<>(taskId, type);
}
}
private static class BuilderC0 extends BaseRefs implements TaskBuilderC0 {
BuilderC0(TaskId taskId, Class type) {
super(taskId, type);
}
@Override
public TaskBuilderC in(F0> aTask) {
F0> aTaskSingleton = Singleton.create(aTask);
return new BuilderC<>(
lazyFlatten(inputs, lazyList(aTaskSingleton)),
taskId, type,
leafEval(
taskId,
tc -> tc.evaluate(aTaskSingleton.get())));
}
@Override
public TaskBuilderC, Z, Z> ins(F0>> aTasks) {
F0>> aTasksSingleton = Singleton.create(aTasks);
return new BuilderC<>(
lazyFlatten(inputs, lazyFlatten(aTasksSingleton)),
taskId, type,
leafEval(
taskId,
tc -> aTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList())));
}
}
private static class BuilderCV0 extends BaseRefs implements TaskBuilderCV0 {
BuilderCV0(TaskId taskId, Class type) {
super(taskId, type);
}
@Override
public TaskBuilderCV, Z> in(F0> aTask) {
F0> aTaskSingleton = Singleton.create(aTask);
return new BuilderCV<>(
lazyFlatten(inputs, lazyList(aTaskSingleton)),
taskId, type,
leafValEval(
taskId,
tc -> tc.evaluate(aTaskSingleton.get())));
}
@Override
public TaskBuilderCV, Value, Z> ins(F0>> aTasks) {
F0>> aTasksSingleton = Singleton.create(aTasks);
return new BuilderCV<>(
lazyFlatten(inputs, lazyFlatten(aTasksSingleton)),
taskId, type,
leafValEval(
taskId,
tc -> aTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList())));
}
}
// #############################################################################################
private static class BuilderC extends BaseRefs implements TaskBuilderC {
private final RecursiveEval evaluator;
private BuilderC(
F0>> inputs, TaskId taskId, Class type, RecursiveEval evaluator) {
super(inputs, taskId, type);
this.evaluator = evaluator;
}
@Override
public Task process(F1 fn) {
return Task.create(inputs, type, evaluator.enclose(fn), taskId);
}
@Override
public TaskBuilderC, Z> in(F0> bTask) {
F0> bTaskSingleton = Singleton.create(bTask);
return new BuilderC<>(
lazyFlatten(inputs, lazyList(bTaskSingleton)),
taskId, type,
evaluator.curry(
tc -> tc.evaluate(bTaskSingleton.get())));
}
@Override
public TaskBuilderC, F1, Z> ins(F0>> bTasks) {
F0>> bTasksSingleton = Singleton.create(bTasks);
return new BuilderC<>(
lazyFlatten(inputs, lazyFlatten(bTasksSingleton)),
taskId, type,
evaluator.curry(
tc -> bTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList())));
}
}
private static class BuilderCV extends BaseRefs implements TaskBuilderCV {
private final RecursiveEval evaluator;
private BuilderCV(
F0>> inputs, TaskId taskId, Class type, RecursiveEval evaluator) {
super(inputs, taskId, type);
this.evaluator = evaluator;
}
@Override
public Task process(F1> code) {
EvalClosure closure = tc -> evaluator.enclose((a) -> code.apply(tc).apply(a)).eval(tc);
return Task.create(inputs, type, closure, taskId);
}
@Override
public TaskBuilderCV, Z> in(F0> bTask) {
F0> bTaskSingleton = Singleton.create(bTask);
return new BuilderCV<>(
lazyFlatten(inputs, lazyList(bTaskSingleton)),
taskId, type,
evaluator.curry(
tc -> tc.evaluate(bTaskSingleton.get())));
}
@Override
public TaskBuilderCV, F1, Z> ins(F0>> bTasks) {
F0>> bTasksSingleton = Singleton.create(bTasks);
return new BuilderCV<>(
lazyFlatten(inputs, lazyFlatten(bTasksSingleton)),
taskId, type,
evaluator.curry(
tc -> bTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList())));
}
}
// #############################################################################################
private static class Builder1 extends BaseRefs implements TaskBuilder1 {
private final ChainingEval> evaluator;
private final ChainingEval>> valEvaluator;
Builder1(
F0>> inputs,
TaskId taskId,
Class type,
ChainingEval> evaluator,
ChainingEval>> valEvaluator) {
super(inputs, taskId, type);
this.evaluator = evaluator;
this.valEvaluator = valEvaluator;
}
@Override
public Task process(F1 code) {
return Task.create(inputs, type, evaluator.enclose(code), taskId);
}
@Override
public Task processWithContext(F2> code) {
EvalClosure closure = tc -> valEvaluator.enclose((a) -> code.apply(tc, a)).eval(tc);
return Task.create(inputs, type, closure, taskId);
}
@Override
public TaskBuilder2 in(F0> bTask) {
F0> bTaskSingleton = Singleton.create(bTask);
return new Builder2<>(
lazyFlatten(inputs, lazyList(bTaskSingleton)),
taskId, type,
evaluator.chain(tc -> {
Value bValue = tc.evaluate(bTaskSingleton.get());
return f2 -> bValue.map(b -> (a) -> f2.apply(a, b));
}),
valEvaluator.chain(tc -> {
Value bValue = tc.evaluate(bTaskSingleton.get());
return f2 -> bValue.map(b -> (a) -> f2.apply(a, b));
}));
}
@Override
public TaskBuilder2, Z> ins(F0>> bTasks) {
F0>> bTasksSingleton = Singleton.create(bTasks);
return new Builder2<>(
lazyFlatten(inputs, lazyFlatten(bTasksSingleton)),
taskId, type,
evaluator.chain(tc -> {
Value> bListValue = bTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList());
return f2 -> bListValue.map(b -> (a) -> f2.apply(a, b));
}),
valEvaluator.chain(tc -> {
Value> bListValue = bTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList());
return f2 -> bListValue.map(b -> (a) -> f2.apply(a, b));
}));
}
}
// #############################################################################################
private static class Builder2 extends BaseRefs implements TaskBuilder2 {
private final ChainingEval> evaluator;
private final ChainingEval>> valEvaluator;
Builder2(
F0>> inputs,
TaskId taskId,
Class type,
ChainingEval> evaluator,
ChainingEval>> valEvaluator) {
super(inputs, taskId, type);
this.evaluator = evaluator;
this.valEvaluator = valEvaluator;
}
@Override
public Task process(F2 code) {
return Task.create(inputs, type, evaluator.enclose(code), taskId);
}
@Override
public Task processWithContext(F3> code) {
EvalClosure closure = tc -> valEvaluator.enclose((a, b) -> code.apply(tc, a, b)).eval(tc);
return Task.create(inputs, type, closure, taskId);
}
@Override
public TaskBuilder3 in(F0> cTask) {
F0> cTaskSingleton = Singleton.create(cTask);
return new Builder3<>(
lazyFlatten(inputs, lazyList(cTaskSingleton)),
taskId, type,
evaluator.chain(tc -> {
Value cValue = tc.evaluate(cTaskSingleton.get());
return f2 -> cValue.map(c -> (a, b) -> f2.apply(a, b, c));
}),
valEvaluator.chain(tc -> {
Value cValue = tc.evaluate(cTaskSingleton.get());
return f2 -> cValue.map(c -> (a, b) -> f2.apply(a, b, c));
}));
}
@Override
public TaskBuilder3, Z> ins(F0>> cTasks) {
F0>> cTasksSingleton = Singleton.create(cTasks);
return new Builder3<>(
lazyFlatten(inputs, lazyFlatten(cTasksSingleton)),
taskId, type,
evaluator.chain(tc -> {
Value> cListValue = cTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList());
return f3 -> cListValue.map(c -> (a, b) -> f3.apply(a, b, c));
}),
valEvaluator.chain(tc -> {
Value> cListValue = cTasksSingleton.get()
.stream().map(tc::evaluate).collect(tc.toValueList());
return f3 -> cListValue.map(c -> (a, b) -> f3.apply(a, b, c));
}));
}
}
// #############################################################################################
private static class Builder3 extends BaseRefs implements TaskBuilder3 {
private final ChainingEval> evaluator;
private final ChainingEval>> valEvaluator;
Builder3(
F0>> inputs,
TaskId taskId,
Class type,
ChainingEval> evaluator,
ChainingEval>> valEvaluator) {
super(inputs, taskId, type);
this.evaluator = evaluator;
this.valEvaluator = valEvaluator;
}
@Override
public Task process(F3 code) {
return Task.create(inputs, type, evaluator.enclose(code), taskId);
}
@Override
public Task processWithContext(F4> code) {
EvalClosure closure = tc -> valEvaluator.enclose((a, b, c) -> code.apply(tc, a, b, c)).eval(tc);
return Task.create(inputs, type, closure, taskId);
}
}
// #############################################################################################
/**
* A convenience class for holding some reference. This is only so that we don't have to repeat
* these declaration in every class above.
*/
private static class BaseRefs {
protected final F0>> inputs;
protected final TaskId taskId;
protected final Class type;
protected BaseRefs(TaskId taskId, Class type) {
this(Collections::emptyList, taskId, type);
}
protected BaseRefs(F0>> inputs, TaskId taskId, Class type) {
this.inputs = inputs;
this.taskId = taskId;
this.type = type;
}
}
// #############################################################################################
private static RecursiveEval leafEval(
TaskId taskId,
EvalClosure aClosure) {
return new RecursiveEval<>(true, taskId, aClosure, taskContext -> taskContext::immediateValue);
}
private static RecursiveEval, B> leafValEval(
TaskId taskId,
EvalClosure aClosure) {
return new RecursiveEval<>(true, taskId, aClosure, taskContext -> val -> val);
}
private static ChainingEval leafEvalFn(F1>> fClosure) {
return new ChainingEval<>(fClosure);
}
private static final class RecursiveEval implements Serializable {
private final boolean leaf;
private final TaskId taskId;
private final EvalClosure aClosure;
private final F1>> contClosure;
RecursiveEval(
boolean leaf,
TaskId taskId,
EvalClosure aClosure,
F1>> contClosure) {
this.leaf = leaf;
this.taskId = taskId;
this.aClosure = aClosure;
this.contClosure = contClosure;
}
public EvalClosure enclose(F1 fn) {
return taskContext -> continuation(taskContext).apply(fn);
}
public RecursiveEval, Z> curry(EvalClosure tClosure) {
return new RecursiveEval<>(false, taskId, tClosure, this::continuation);
}
private F1, Value> continuation(TaskContext taskContext) {
F1> cont = contClosure.apply(taskContext);
Value aVal = aClosure.eval(taskContext);
return fn -> aVal.flatMap((a) -> (leaf)
? taskContext.invokeProcessFn(taskId, () -> cont.apply(fn.apply(a)))
: cont.apply(fn.apply(a)));
}
}
private static final class ChainingEval implements Serializable {
private final F1>> fClosure;
ChainingEval(F1>> fClosure) {
this.fClosure = fClosure;
}
public EvalClosure enclose(F f) {
//noinspection unchecked
return taskContext -> (Value) fClosure.apply(taskContext).apply(f);
}
public ChainingEval chain(F1>> mapClosure) {
F1>> continuation = tc -> {
F1> fng = mapClosure.apply(tc);
F1> fnf = fClosure.apply(tc);
return g -> fng.apply(g).flatMap(fnf::apply);
};
return new ChainingEval<>(continuation);
}
}
private static F1> gated(TaskId taskId, TaskContext tc, F1 f1) {
return (a) -> tc.invokeProcessFn(taskId, () -> tc.immediateValue(f1.apply(a)));
}
private static F1> gatedVal(TaskId taskId, TaskContext tc, F1> f1) {
return (a) -> tc.invokeProcessFn(taskId, () -> f1.apply(a));
}
private static EvalClosure gated(TaskId taskId, F0 code) {
return tc -> tc.invokeProcessFn(taskId, () -> tc.value(code));
}
private static EvalClosure gatedVal(TaskId taskId, F1> code) {
return tc -> tc.invokeProcessFn(taskId, () -> code.apply(tc));
}
/**
* Converts an array of {@link F0}s of {@link Task}s to a {@link F0} of a list of
* those tasks {@link Task}s.
*
* It will only evaluate the functions (through calling {@link F0#get()})
* when the returned function is invoked. Thus it retains laziness.
*
* @param tasks An array of lazy evaluated tasks
* @return A function of a list of lazily evaluated tasks
*/
@SafeVarargs
private static F0>> lazyList(F0 extends Task>>... tasks) {
return () -> Stream.of(tasks)
.map(F0::get)
.collect(toList());
}
@SafeVarargs
private static F0> lazyFlatten(F0 extends List extends T>>... lists) {
return () -> Stream.of(lists)
.map(F0::get)
.flatMap(List::stream)
.collect(toList());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy