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

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