Yes, the former is more efficient and the latter more comprehensible. But there is a simple step which can reconcile both approaches.
For that, we have to add some functions, so we can write :
reduce([0,1,2,3,4]).map(inc).filter(isEven).into(array);
The names `reduce`, `into` and `array` may surely be improved; but they convey the idea well enough.
The `reduce` function takes an iterable object and turns it into a reducible object i.e. an object with an `into` method to which will be provided all the stuff required to reduce the content of the former iterable into a new array, a sum or whatever result which can be obtained adding items one after the other into a seed.
Note that the `reduce` function doesn't iterate over its argument.
Neither do the `map` and `filter` methods. Along the chain the iterable is simply wrapped with functions and filters to be used latter.
All computations occure when an actual reducer is provided through a call to the `into` method.
Then the mapping and filtering arguments are used to transform the given reducer into a new specific reducer.
(this is why they are called transducers). And then the iterable is reduced using some loop like the one you show.
So transducers can be wrapped to be used like regular filters over collections.
The beauty of transducers is that they express efficient transformation chains which do not depend of actual input and output.
By the way, it seems to me that the proposed javascript transducers miss the last point : the proposed `into` function takes an implied reducer which is computed after the input. An array is always reduced to an array ! What about reduction into a string or a sum ?
Last remark. Bellow a reducible has to wrap a transducer and the code of `into` has to check if there is actually a transducer (chaining mapping and filtering).
This is a bit ugly. I think this is due to an over emphasis on transducers. The code would be simpler if we were transforming reducibles either reducers.
-------
transducers.Reducible = function(coll, reduce, transducer) {
this.coll = coll;
this.reduce = reduce;
this.transducer = transducer;
};
transducers.Reducible.prototype.into = function(xf) {
if (this.transducer == null) {
return this.reduce(xf, xf.init(), this.coll);
} else {
transduced_xf = this.transducer(xf);
return this.reduce(transduced_xf, transduced_xf.init(), this.coll);
}
};
transducers.Reducible.prototype.comp = function(other_transducer) {
if (this.transducer == null) {
return other_transducer;
} else {
return transducers.comp(this.transducer, other_transducer);
}
};
transducers.Reducible.prototype.map = function(f) {
new_transducer = this.comp( transducers.map(f) );
return new transducers.Reducible(this.coll, this.reduce, new_transducer);
};
transducers.Reducible.prototype.filter = function(pred) {
new_transducer = this.comp( transducers.filter(pred) );
return new transducers.Reducible(this.coll, this.reduce, new_transducer);
};
transducers.reduce = function(coll) {
if(transducers.isString(coll)) {
return new transducers.Reducible(coll, transducers.stringReduce, null);
} else if(transducers.isArray(coll)) {
return new transducers.Reducible(coll, transducers.arrayReduce, null);
} else if(transducers.isIterable(coll)) {
return new transducers.Reducible(coll, transducers.iterableReduce, null);
} else if(transducers.isObject(coll)) {
return new transducers.Reducible(coll, transducers.objectReduce, null);
} else {
throw new Error("Cannot reduce instance of " + coll.constructor.name);
}
};
transducers.array = {}
transducers.array.init = function() { return []; };
transducers.array.result = function(result) { return result; };
transducers.array.step = function(result, input) { result.push(input); return result; };
transducers.sum = {}
transducers.sum.init = function() { return 0; };
transducers.sum.result = function(result) { return result; };
transducers.sum.step = function(result, input) { return result + input; };