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.
Iterable
/ArrayIterator
/StringIterator
interfaces you are referring to? Are they from some standard javascript framework? Have you defined them yourself? – BenedictionArrayIterator
orStringIterator
prototype, there are iteration protocols: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… – Flour[].values()
logsArrayIterator {}
in my chromium browser. Is this merely chrome specific behavior? – Joist("")[Symbol.iterator]()
logsStringIterator {}
. – JoistObject.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())).isPrototypeOf([].values())
indeed yieldstrue
, as well as("")[Symbol.iterator]()
(instead of[].values()
). Hence we can't distinguish them by their prototype. – Joistmap
function). And the implementation isn't self-documented to explain this,foldl
function just adds extra complexity and doesn't really help to get the gist of it. If you're trying the guess the proper constructor for relevant iterator (instead of[]
), the only way to do this is to stringify an iterator... looks fragile to me. – Disyllable