Consider the following implementation of a "zip" transformer:
function zip() {
return xf => Zip(xf);
}
const sub = Symbol('sub');
function Zip(xf) {
return {
['@@transducer/init']() {
const result = { [sub]: [] };
// if init is not implemented in wrapped, ignore
try {
result.wrapped = xf['@@transducer/init']();
}
catch(err) { }
return result;
},
['@@transducer/result'](result) {
if(result[sub] == null || result[sub].length === 0) {
return result.wrapped || result;
}
const wrappedResult = result[sub][0].reduce((acc, input, i)=>
xf['@@transducer/step'](acc, result[sub].map((a)=>a[i]))
, result.wrapped);
return xf['@@transducer/result'](wrappedResult);
},
['@@transducer/step'](result, input) {
if(result[sub] == null) {
// "into" - init not called
const acc = this['@@transducer/init']();
// pass the evil on to the wrapped accumulator
acc.wrapped = result;
result = acc;
}
result[sub].push(input);
return result;
}
};
}
It "works" but does it by hackery. What it should do is create an accumulator object in init, then accumulate the subcollections. On finalization, it can feed the final objects to the downstream transformer.
I propose that @@transducer/init be responsible to create and return an accumulator object which wraps the downstream transformer accumulator. It could have signature:
(Here xf is the downstream transformer -- could be this.xf depending on implementation.)
By default, as in zip, we could use the accumulator to store state. Eg. we could have a transducer that was calculating a histogram, then forwarding the bins onwards (etc.).
If an accumulator did wrap downstream state in its own state, it is then responsible for unwrapping the downstream state in step and result.
finalAccumulator is the thing into which the whole pipeline is writing. Normally we ignore it,
but special end of chain "output aware" transformers could use it (provided by the library).
Consider the following implementation of a "zip" transformer:
It "works" but does it by hackery. What it should do is create an accumulator object in
init
, then accumulate the subcollections. On finalization, it can feed the final objects to the downstream transformer.I propose that
@@transducer/init
be responsible to create and return an accumulator object which wraps the downstream transformer accumulator. It could have signature:With default implementation:
(Here
xf
is the downstream transformer -- could bethis.xf
depending on implementation.)By default, as in zip, we could use the accumulator to store state. Eg. we could have a transducer that was calculating a histogram, then forwarding the bins onwards (etc.).
If an accumulator did wrap downstream state in its own state, it is then responsible for unwrapping the downstream state in
step
andresult
.finalAccumulator
is the thing into which the whole pipeline is writing. Normally we ignore it, but special end of chain "output aware" transformers could use it (provided by the library).