in scalding-dagon/src/main/scala/com/twitter/scalding/dagon/Expr.scala [89:145]
def evaluate[N[_], T](idToExp: HMap[Id, Expr[N, *]], expr: Expr[N, T]): N[T] =
evaluateMemo(idToExp)(expr)
/**
* Build a memoized FunctionK for this particular idToExp. Clearly, this FunctionK is only valid for the
* given idToExp which is captured in this closure.
*/
def evaluateMemo[N[_]](idToExp: HMap[Id, Expr[N, *]]): FunctionK[Expr[N, *], N] = {
val fast = Memoize.functionK[Expr[N, *], N](new Memoize.RecursiveK[Expr[N, *], N] {
def toFunction[T] = {
case (Const(n), _) => n
case (Var(id), rec) => rec(idToExp(id))
case (Unary(id, fn), rec) =>
fn(rec(idToExp(id)))
case (Binary(id1, id2, fn), rec) =>
fn(rec(idToExp(id1)), rec(idToExp(id2)))
case (Variadic(args, fn), rec) =>
fn(args.map(id => rec(idToExp(id))))
}
})
import TailCalls._
val slowAndSafe = Memoize.functionKTailRec[Expr[N, *], N](new Memoize.RecursiveKTailRec[Expr[N, *], N] {
def toFunction[T] = {
case (Const(n), _) => done(n)
case (Var(id), rec) => rec(idToExp(id))
case (Unary(id, fn), rec) => rec(idToExp(id)).map(fn)
case (Binary(id1, id2, fn), rec) =>
for {
nn1 <- rec(idToExp(id1))
nn2 <- rec(idToExp(id2))
} yield fn(nn1, nn2)
case (Variadic(args, fn), rec) =>
def loop[A](as: List[Id[A]]): TailRec[List[N[A]]] =
as match {
case Nil => done(Nil)
case h :: t => loop(t).flatMap(tt => rec(idToExp(h)).map(_ :: tt))
}
loop(args).map(fn)
}
})
def onStackGoSlow[A](lit: Expr[N, A]): N[A] =
try fast(lit)
catch {
case _: Throwable => // StackOverflowError should work, but not on scala.js
slowAndSafe(lit).result
}
/*
* We *non-recursively* use either the fast approach or the slow approach
*/
Memoize.functionK[Expr[N, *], N](new Memoize.RecursiveK[Expr[N, *], N] {
def toFunction[T] = { case (u, _) => onStackGoSlow(u) }
})
}