Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: @@transducer/init should always be called, create an accumulator object. #46

Open
shaunc opened this issue Apr 25, 2017 · 1 comment

Comments

@shaunc
Copy link

shaunc commented Apr 25, 2017

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:

['@@transducer/init'](finalAccumulator) { }

With default implementation:

['@@transducer/init'](finalAccumulator) { return xf['@@transducer/init](finalAccumulator); }

(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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants