I wrote a reduce
function for Iterable
s and now I want to derive a generic map
that can map over arbitrary Iterable
s. However, I have encountered an issue: Since Iterable
s abstract the data source, map
couldn't determine the type of it (e.g. Array
, String
, Map
etc.). I need this type to invoke the corresponding identity element/concat function. Three solutions come to mind:
- pass the identity element/concat function explicitly
const map = f => id => concat => xs
(this is verbose and would leak internal API though) - only map
Iterable
s that implement the monoid interface (that were cool, but introducing new types?) - rely on the prototype or constructor identity of
ArrayIterator
,StringIterator
, etc.
I tried the latter but isPrototypeOf
/instanceof
always yield false
no matter what a do, for instance:
Array.prototype.values.prototype.isPrototypeOf([].values()); // false
Array.prototype.isPrototypeOf([].values()); // false
My questions:
- Where are the prototypes of
ArrayIterator
/StringIterator
/...? - Is there a better approach that solves the given issue?
Edit: [][Symbol.iterator]()
and ("")[Symbol.iterator]()
seem to share the same prototype:
Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) ====
Object.getPrototypeOf(Object.getPrototypeOf(("")[Symbol.iterator]()))
A distinction by prototypes seems not to be possible.
Edit: Here is my code:
const values = o => keys(o).values();
const next = iter => iter.next();
const foldl = f => acc => iter => {
let loop = (acc, {value, done}) => done
? acc
: loop(f(acc) (value), next(iter));
return loop(acc, next(iter));
}
// static `map` version only for `Array`s - not what I desire
const map = f => foldl(acc => x => [...acc, f(x)]) ([]);
console.log( map(x => x + x) ([1,2,3].values()) ); // A
console.log( map(x => x + x) (("abc")[Symbol.iterator]()) ); // B
The code in line A
yields the desired result. However B
yields an Array
instead of String
and the concatenation only works, because String
s and Number
s are coincidentally equivalent in this regard.
Edit: There seems to be confusion for what reason I do this: I want to use the iterable/iterator protocol to abstract iteration details away, so that my fold/unfold and derived map/filter etc. functions are generic. The problem is, that you can't do this without also having a protocol for identity/concat. And my little "hack" to rely on prototype identity didn't work out.
@redneb made a good point in his response and I agree with him that not every iterable is also a "mappable". However, keeping that in mind I still think it is meaningful - at least in Javascript - to utilize the protocol in this way, until maybe in future versions there is a mappable or collection protocol for such usage.