Initial add of the generic optimizer code

Before digging into this code, look at the Rules implemented at the bottom of DagOptimizer.

There are several TODOS here:

- [x] After I split the code into separate files, I started hitting a scala 2.9 compiler bug, that I don't hit in 2.10. I'll try to work around, or possibly hope that Twitter finally gets on 2.9 before we need this.

- [x] Tests tests tests. I played around at the repl, but clearly we need super hardcore tests for any optimization framework.

- [ ] Actually call this code from an existing platform.
Ok, this applies to both... it's a little gnarly, and I will check out your code in a sec and try it, but there are two ways to eliminate the use of the full lambda. If the first one works I definitely prefer it, but we have to make sure the paths are consistent. Either way...

```
// Works in 2.11, should work in 2.10, works in 2.9 with -Xexperimental
// Need to branch yours and make sure that the path dependent type works like I think it should

object HMap {
  def empty[K[_], V[_]]: HMap[K, V] = sys.error("compiled onlye")
}
trait HMap[K[_], V[_]]

final case class Id[T](id: Int)

sealed trait Expr[T, N[_]] {
  type E[t] = Expr[t, N]
  def evaluate(idToExp: HMap[Id, E]): N[T] =
    Expr.evaluate(this)(idToExp)
}
case class Const[T, N[_]](value: N[T]) extends Expr[T, N] {
  override def evaluate(idToExp: HMap[Id, E]): N[T] = value
}
case class Var[T, N[_]](name: Id[T]) extends Expr[T, N]
case class Unary[T1, T2, N[_]](arg: Id[T1], fn: N[T1] => N[T2]) extends Expr[T2, N]
case class Binary[T1, T2, T3, N[_]](arg1: Id[T1],
  arg2: Id[T2],
  fn: (N[T1], N[T2]) => N[T3]) extends Expr[T3, N]

object Expr {
  def evaluate[T, N[_]](expr: Expr[T, N])(idToExp: HMap[Id, expr.E]): N[T] = inEvaluate(expr)(idToExp, HMap.empty)._2

  private def inEvaluate[T, N[_]](expr: Expr[T, N])(idToExp: HMap[Id, expr.E],cache: HMap[expr.E, N]): (HMap[expr.E, N], N[T]) = sys.error("compiled only")
}

// Doesn't use path dependent types
object HMap {
  def empty[K[_], V[_]]: HMap[K, V] = sys.error("compiled onlye")
}
trait HMap[K[_], V[_]]

final case class Id[T](id: Int)

sealed trait Expr[T, N[_]] {
  type E[t] = Expr[t, N]
  def evaluate(idToExp: HMap[Id, E]): N[T] =
    Expr.evaluate(idToExp, this)
}
case class Const[T, N[_]](value: N[T]) extends Expr[T, N] {
  override def evaluate(idToExp: HMap[Id, E]): N[T] = value
}
case class Var[T, N[_]](name: Id[T]) extends Expr[T, N]
case class Unary[T1, T2, N[_]](arg: Id[T1], fn: N[T1] => N[T2]) extends Expr[T2, N]
case class Binary[T1, T2, T3, N[_]](arg1: Id[T1],
  arg2: Id[T2],
  fn: (N[T1], N[T2]) => N[T3]) extends Expr[T3, N]

object Expr {
  type E[N[_]] = {type L[t] = Expr[Any, N]#E[t] }
  def evaluate[T, N[_]](idToExp: HMap[Id, E[N]#L], expr: Expr[T, N]): N[T] = evaluate(idToExp, HMap.empty[E[N]#L, N], expr)._2

  private def evaluate[T, N[_]](idToExp: HMap[Id, E[N]#L], cache: HMap[E[N]#L, N], expr: Expr[T, N]): (HMap[E[N]#L, N], N[T]) = sys.error("compiled only")
}
```