A forEach can easily return a transformed list of values.
acc = []
for el in values:
acc.append(f(el))
return acc
This hierarchy doesn't exist; you can pretty much implement any of them in terms of the others.
To make that untrue, you need to define very strict limits on what else your language can do.
(Sure, we produce a transformed list here through the use of side effects. But note that the original example sparking the claim that forEach can't do what map can was this:
The forEach isn't returning it in your example, the line after it is returning. Try again without acc and I believe that's what was meant.
You can implement any of them in terms of the others, but only if you break convention and introduce side effects. Follow convention and try to implement (for example) reduce with map and you'll find that it's not possible.
> You can implement any of them in terms of the others, but only if you break convention and introduce side effects.
This can't be right. forEach has no effects other than side effects. A convention that says not to use side effects prevents you from using forEach at all.
But that would make claims about the place of forEach in a hierarchy into meaningless nonsense.
> The forEach isn't returning it in your example, the line after it is returning.
That depends on your point of view. C has numerous functions which accept pointer parameters and return values in those parameters. That's just the normal way to return multiple values in C.
And by that standard, the forEach itself is returning the transformed list; that's where the transformation occurs. The following `return` line is only necessary if this is a snippet within a function whose purpose is to execute a for statement; you would actually do this inline, by just writing the for statement without needing a following `return`.
I think they’re only talking about JavaScript, in which case the forEach function doesn’t return anything.
So you can do x.map(…).forEach(…) because map returns an array. But u can’t do x.forEach(…).map(…)
Ur examples seem like they’re for a different language.
Haskell has strict limits on side-effects, but also abstracts these functions over the thing you're "forEaching." There, a reduce can't implement a map, because a map is required to return the same type of collection you put in. A reduce can only return an arbitrarily chosen collection (say, an Array).
And without side-effects, a map can't implement a forEach.
> And without side-effects, a map can't implement a forEach.
Yes, it can, because without side effects, forEach is a NOP. That's not difficult to implement. As long as you don't do anything with the return value from map, you're there.
I think it's just being confusingly stated. GP already said that reduce could implement map. I think it's merely that reduce can just output a single result, of any type. (Which is already more powerful than map, which must output an array.)
Of course, in fact you could hack anything in any of these, since you could be doing other work in the function called. But I think the general principle is sound.
forEach is actually more powerful than map, you would not be able to use map for side effects in a static and/or lazy language. In a strict, dynamic language, it matters less. Even so I wouldn't describe being an expression vs statement as a difference in power. It's contextual.
I take it you've never read any Python? That's a forEach. Python doesn't even have a for loop construct (though it does have while, which is equivalent).
> And depending on the language I'm pretty sure that return call would exit the parent scope.
Yes, that's the point, that's an illustration of terminating the forEach before you've processed each element.
I'm not very proficient in Python, no. I thought you were giving pseudo-code examples. If your argument doesn't apply to the common array methods (forEach, map, reduce) in JS and similar languages, then you missed the entire point of this comment thread.
If you have a C-style for-loop, you can and have to do everything yourself. That means you can skip elements or processer them twice, etc.
In a Python-style for-each loop, you can break out with break or return, but you have a harder time skipping or changing the order of processing. So they are weaker. And that's good.
(Your examples still process elements in the body of the loop. It's just that sometimes the body decides to do a no-op.)
Putting the no-op logic in the body of the loop, or in the function you pass to reduce is different than being able to short-cut evaluation.
You can see the difference most clearly, when trying to process (the start of) an infinite generator with reduce or a for-loop. Reduce will just hang.
A for loop can implement a reduce, a reduce can implement a map, a map can implement a forEach, but not the inverse.