in scalding-dagon/src/main/scala/com/twitter/scalding/dagon/Literal.scala [47:100]
def evaluate[N[_], T](lit: Literal[N, T]): N[T] =
evaluateMemo[N](lit)
/**
* Memoized version of evaluation to handle diamonds
*
* Each call to this creates a new internal memo.
*/
def evaluateMemo[N[_]]: FunctionK[Literal[N, *], N] = {
import TailCalls._
val slowAndSafe =
Memoize.functionKTailRec[Literal[N, *], N](new Memoize.RecursiveKTailRec[Literal[N, *], N] {
def toFunction[T] = {
case (Const(n), _) => done(n)
case (Unary(n, fn), rec) => rec(n).map(fn)
case (Binary(n1, n2, fn), rec) =>
for {
nn1 <- rec(n1)
nn2 <- rec(n2)
} yield fn(nn1, nn2)
case (Variadic(args, fn), rec) =>
def loop[A](as: List[Literal[N, A]]): TailRec[List[N[A]]] =
as match {
case Nil => done(Nil)
case h :: t => loop(t).flatMap(tt => rec(h).map(_ :: tt))
}
loop(args).map(fn)
}
})
val fast = Memoize.functionK[Literal[N, *], N](new Memoize.RecursiveK[Literal[N, *], N] {
def toFunction[T] = {
case (Const(n), _) => n
case (Unary(n, fn), rec) => fn(rec(n))
case (Binary(n1, n2, fn), rec) => fn(rec(n1), rec(n2))
case (Variadic(args, fn), rec) => fn(args.map(rec(_)))
}
})
def onStackGoSlow[A](lit: Literal[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[Literal[N, *], N](new Memoize.RecursiveK[Literal[N, *], N] {
def toFunction[T] = { case (u, _) => onStackGoSlow(u) }
})
}