Fixing Broadcast

Reimplement `Broadcast` operation for fusing scalar expressions over collections.

## Broadcast docstring

Given elementwise operations on collections, e.g.
```Python
>>> a = Symbol('a', '100 * int')
>>> t = Symbol('t', '100 * {x: int, y: int}')

>>> expr = sin(a) + t.y**2
```
It may be best to represent this as a scalar expression mapped over a
collection
```Python
>>> sa = Symbol('a', 'int')
>>> st = Symbol('t', '{x: int, y: int}')

>>> sexpr = sin(sa) + st.y**2

>>> expr = Broadcast((a, t), (sa, st), sexpr)
```
This provides opportunities for optimized computation.

In practice, expressions are often collected into Broadcast expressions
automatically.  This class is mainly intented for internal use.

## Optimization issues

Generally we don't construct `Broadcast`s by hand.  Instead, there are a few scattered functions to collect up operations of various types automatically using the somewhat new-and-experimental `optimize` framework.  There are a few of these due to slight differences in how the backends want operations bundled up.  Probably this should be cleaned up a bit.

## TODO

- [ ] Optimize should work on subtrees
    currently we only call `expr = optimize(expr, *leaves)` at the onset of `compute`.  In cases where we have multiple leaves (as in a join or binop) then this often fails, because we usually only define `optimize` for a single data input type.  Really we should call this down the tree to optimize subtrees with a single leaf.
- [ ] Nested Python data structures
- [ ] Intelligent handling (or at least erring) in Python cases where we might consume an iterator twice
- [ ] numexpr support in numpy
does this chain of `isinstance` checks introduce an unnecessary order dependency?

is this meant to never be extended?

if not then it's ok, otherwise i wonder if MD might be especially useful here in helping us not have to keep track of where we put a new expression in this chain of conditionals