def evaluate[N[_], T]()

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) }
    })
  }