add named outputs and clean up dag.Parallel
While refactoring the optimizer, we realized the code would be simpler if we required all paths of a dag.Parallel to be dag.Sequential instead of dag.Op. Also, parallel implements two semantics --- multiple concurrent outputs as well as a merge/combine to downstream ops. These two cases should be separated by having separate Ops and Taps slices where the Tap can be a named output. Changing outputs from integer channel IDs to names should not break zui right now as it ignores the channel IDs and treats the results as one integrated collection.
This will let us do things like
... | output foo | ...
which would create a
dag.Parallel{
Taps: []dag.Tap{&dag.Tap{"foo"}}
Ops: []dag.Op{dag.Pass{}}
}
The parents to the downstream ops would be the Ops and the Tap would show up to the client as named outputs.
You could also do something like this:
fork (
=> ... | output A
=> ... | output B
)
For dataflow that never hits an output and gets to the end of the DAG, the data would be placed on output main.