-
Notifications
You must be signed in to change notification settings - Fork 135
X Eval
Eval allows us to chain operations that are evaluated lazily (or lazily with caching / memoization). Eval is a stack-safe control type, tail-recursive calls to Eval within map / flatMap operations are trampolined. Eval can be used to safely implement recursive algorithms in Java.
now creates an Eval eagerly, but all chained operations are deferred / lazy
Eval<Integer> eager = Eval.now(10);
Eval<Integer> lazy = eager.map(i->i*2);
later creates an Eval lazily, that will be evaluated only once no matter how many times get is called
Eval<Integer> lazy = Eval.later(this::expensiveMethod);
lazy.get(); //triggers expensiveMethod
lazy.get(); //returns cached value
Subsequent map / flatMap calls are also evaluated only once (and lazily)
always creates an Eval lazily, but is evaluated repeatedly with get() is called
Eval<Integer> lazy = Eval.always(this::expensiveMethod);
lazy.get(); //triggers expensiveMethod
lazy.get(); //triggers expensiveMethod
The map / flatMap operators in Eval convert recursive tail calls to iteration.
public void runFib(){
System.out.println(fibonacci(Eval.now(tuple(100_000,1l,0l))));
}
public Eval<Long> fibonacci(Eval<Tuple3<Integer,Long,Long>> fib) {
return fib.flatMap(t->t.v1 == 0 ? Eval.now(t.v3) : fibonacci(Eval.now(tuple(t.v1-1,t.v2+t.v3,t.v2))));
}
The above code can be rewritten to use Suppliers in the method signature instead
public void runFib(){
System.out.println(fibonacci(Eval.now(tuple(100_000,1l,0l)).get()));
}
public Supplier<Long> fibonacci(Supplier<Tuple3<Integer,Long,Long>> fib) {
return Eval.eval(fib).flatMap(t->t.v1 == 0 ? Eval.now(t.v3) : fibonacci(Eval.now(tuple(t.v1-1,t.v2+t.v3,t.v2))));
}
The zip method on Eval can be used to Evaluate multiple lazy code blocks concurrently, and only accepts another Eval, Trampoline or Supplier as a parameter.
public void interleave(){
Eval<Integer> algorithm1 = loop(50000,Eval.now(5));
Eval<Integer> algorithm2 = loop2(50000,Eval.now(5));
//interleaved execution via Zip!
Tuple2<Integer, Integer> result = algorithm1.zip(algorithm2,Tuple::tuple).get();
System.out.println(result);
}
Eval<Integer> loop2(int times,Eval<Integer> sum){
System.out.println("Loop-B " + times + " : " + sum);
if(times==0)
return sum;
else
return sum.flatMap(s->loop2(times-1,Eval.now(s+times)));
}
Eval<Integer> loop(int times,Eval<Integer> sum){
System.out.println("Loop-A " + times + " : " + sum);
if(times==0)
return sum;
else
return sum.flatMap(s->loop(times-1,Eval.now(s+times)));
}
Producing output showing executing of interleaved lazy calls
Loop-B 15746 : Always[1126048874]
Loop-A 15745 : Always[1126064620]
Loop-B 15745 : Always[1126064620]
Loop-A 15744 : Always[1126080365]
Loop-B 15744 : Always[1126080365]
Loop-A 15743 : Always[1126096109]
Loop-B 15743 : Always[1126096109]
Loop-A 15742 : Always[1126111852]
Loop-B 15742 : Always[1126111852]
Loop-A 15741 : Always[1126127594]
oops - my bad