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.
N[_] will be TypedPipe[_] or Producer[P, _]. So, within a Dag, the container type is fixed, but each type of contained value is not.

To your question: could it be simplified so that we don't need fix it on an outer container, it might work. This is strictly more general, since `N[T] = T` is a valid case for `N`. But it is not obvious to me if it would work without it. It may well. This could be an artifact of the way that I came about writing the code.