
fj.data.Eval Maven / Gradle / Ivy
package fj.data;
import fj.F;
import fj.F0;
import fj.P;
import fj.P1;
import fj.control.Trampoline;
/**
* Eval
is an abstraction over different models of evaluation.
* The data constructors:
*
* Now
- the value is evaluated immediately.
* Later
- the value is evaluated only once when it's requested (lazy evaluation).
* Always
- the value is evaluated every time when it's requested.
*
*
* Both Later
and Always
are lazy computations, while Now
is eager.
*
*
* @version %build.number%
*/
public abstract class Eval {
/**
* Constructs an eager evaluation by wrapping the given value.
*
* @param a the evaluated value.
* @return an eval with computed value.
*/
public static Eval now(A a) {
return new Now<>(a);
}
/**
* Constructs a lazy evaluation with caching.
*
* @param a the supplier that evaluates a value.
* @return a lazy evaluation.
*/
public static Eval later(F0 a) {
return new Later<>(a);
}
/**
* Constructs a lazy evaluation without caching.
*
* @param a the supplier that evaluates a value.
* @return a lazy evaluation.
*/
public static Eval always(F0 a) {
return new Always<>(a);
}
/**
* Constructs a lazy evaluation of an expression that produces Eval
.
* This operation is stack-safe and can be used for recursive computations.
*
* @param a the supplier that produces an Eval
.
* @return a lazily evaluated nested evaluation.
*/
public static Eval defer(F0> a) {
return new DeferEval<>(a);
}
/**
* Evaluates the computation and return its result.
* Depending on whether the current instance is lazy or eager the
* computation may or may not happen at this point.
*
* @return a result of this computation.
*/
public abstract A value();
/**
* Transforms Eval
into a Eval
using
* the given function.
*
* Note: the computation of the given transformation is always lazy,
* even if it invoked for an eager Now
instance. This computation
* is performed in O(1) stack space.
*
* @param f the transformation function.
* @return a transformed evaluation.
*/
public final Eval map(final F f) {
return bind(a -> now(f.f(a)));
}
/**
* Alias for {@link #bind(F)}.
*/
public final Eval flatMap(final F> f) {
return bind(f);
}
/**
* Transforms Eval
into a Eval
using
* the given function that directly produces Eval
.
*
* Note: the computation of the given transformation is always lazy,
* even if it invoked for an eager Now
instance. This computation
* is performed in O(1) stack space.
*
* @param f the transformation function.
* @return a transformed evaluation.
*/
public final Eval bind(final F> f) {
return new BindTrampolineEval<>(f, asTrampoline());
}
/**
* Transforms the current instance into a trampoline instance.
*/
abstract TrampolineEval asTrampoline();
/**
* Represents an eager computation.
*/
private static final class Now extends Eval {
private final A a;
Now(A a) {
this.a = a;
}
@Override
public final A value() {
return a;
}
@Override
final TrampolineEval asTrampoline() {
return new PureTrampolineEval<>(this);
}
}
/**
* Represents a lazy computation that is evaluated only once.
*/
private static final class Later extends Eval {
private final P1 memo;
Later(F0 producer) {
this.memo = P.hardMemo(producer);
}
@Override
public final A value() {
return memo._1();
}
@Override
final TrampolineEval asTrampoline() {
return new PureTrampolineEval<>(this);
}
}
/**
* Represents a lazy computation that is evaluated every time when it's requested.
*/
private static final class Always extends Eval {
private final F0 supplier;
Always(F0 supplier) {
this.supplier = supplier;
}
@Override
public final A value() {
return supplier.f();
}
@Override
final TrampolineEval asTrampoline() {
return new PureTrampolineEval<>(this);
}
}
/**
* A helper abstraction that allows to perform recursive lazy transformations in O(1) stack space.
*/
private static abstract class TrampolineEval extends Eval {
protected abstract Trampoline trampoline();
@Override
public final A value() {
return trampoline().run();
}
@Override
final TrampolineEval asTrampoline() {
return this;
}
}
private static final class PureTrampolineEval extends TrampolineEval {
private final Eval start;
PureTrampolineEval(Eval start) {
this.start = start;
}
@Override
protected final Trampoline trampoline() {
return Trampoline.suspend(() -> Trampoline.pure(start.value()));
}
}
private static final class BindTrampolineEval extends TrampolineEval {
private final TrampolineEval next;
private final F> f;
BindTrampolineEval(F> f, TrampolineEval next) {
this.next = next;
this.f = f;
}
@Override
protected final Trampoline trampoline() {
return Trampoline.suspend(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline()));
}
}
private static final class DeferEval extends TrampolineEval {
private final P1> memo;
DeferEval(F0> producer) {
this.memo = P.hardMemo(producer);
}
@Override
protected final Trampoline trampoline() {
return Trampoline.suspend(() -> memo._1().asTrampoline().trampoline());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy