7 ms·
FP is about avoiding side-effects; forEach() can only cause side-effects, so it's not really functional at all, and yet it's superficially designed to blend in
by brundolf 5y ago
FP is about avoiding side-effects; forEach() can only cause side-effects, so it's not really functional at all, and yet it's superficially designed to blend in with the actual functional array methods like map().
IMO it's the worst of both worlds and there's virtually never a good reason to use it. If you're going to write a side-effect-y loop, a classic for-loop (or more often, a for..of loop) helps to emphasize that the code is side-effect-y.
- spoils19 5y agoAll most programmers will need is a classic for-loop. For of, forEach and any other kind of iterator is really not necessary and adds to the language bloat that just confuses less experienced developers.
- yakshaving_jgt 5y agoActually, all most programmers need is either `map` or `reduce`. A classic `for` loop is too low-level for most people’s needs.
- Xevi 5y agoI find it really difficult to believe that there are developers out there that never need to be able to break or continue in a loop. What exactly would they be developing?
- yakshaving_jgt 5y agoI’m not sure where you got “never” from in my comment.
- bryanrasmussen 5y agoI believe it was gotten from the ambiguities of English - when you say "all most programmers need is " it could mean all that is needed most of the time but probably the more usual meaning would be that a great number of programmers (most programmers - greater than 50% of programmers), will never need anything but `map` or `reduce`. If the second meaning is taken then this means as a corollary that these developers never need to be able to break or continue in a loop. Of course if it were assumed they never need anything but the higher level methods: map, reduce, some, every etc. - then they would probably use some or every if they wanted to break.
- yakshaving_jgt 5y ago> this means as a corollary that these developers never need to be able to break or continue in a loop. No it doesn’t.
- bryanrasmussen 5y agoI don't understand this no it doesn't, the logic I outlined can be boiled down to If I only need to do X And X cannot do Y Then I do not need to do Y ----------------------------- If I need to do Y And X cannot do Y Then I need to do something other than X ------------------------- Give your answer of no it doesn't please be so kind as to outline the logical reasoning that you are envisioning? on edit: added line between logical statements.
- yakshaving_jgt 5y agoUnless I've misunderstood you, you said that, for example, `continue` cannot be implemented in terms of `map`, which simply isn't true. It's trivial.
- bryanrasmussen 5y agoI thought the original statement was break or continue, I was actually focusing on break? As far as I know you don't break in map, you can return null but not break the loop itself. on edit: although I almost never break in a loop, I use some other construct instead. on second edit: maybe I should say iteration instead of loop but will let it stand.
- yakshaving_jgt 5y agoIt's not clear to me what you're trying to achieve. Sorry.
- bryanrasmussen 5y agoout of the edit window, evidently was typing quickly, but would not use every for implementing a break like functionality on an array - some could do it in some instances or maybe find. I suppose filter could maybe be hacked to do it in some cases dependent on implementation, if the implementation determined that it was impossible to ever return true for any of the items remaining in an array that implementation might effectively break the array processing however every way I can think of doing that would be artificial, horrible, and probably full of side effects.
- just1comment 5y agoCome on, you just said that most people only need map or reduce, which implies they never need to break or continue.
- yakshaving_jgt 5y agoRegardless of how you choose to interpret my comment, it is the case that both `break` and `continue` are literally never necessary, as those two constructs can be implemented in terms of `map` and `reduce`.
- bryanrasmussen 5y agook, how do you implement a break of the iteration in map in JavaScript? I believe it cannot be done, you believe it can? Teach me.
- yakshaving_jgt 5y agoThere is no sensible way to `break` the iteration while mapping. That's not what `map` is for. In fact, there's very little reason for anyone to need `break` ever. The use of `break` implies an effectful procedure rather than a pure function, so at this point you're not really doing Functional Programming. Let me know what it is you're trying to achieve — at a high level, not in terms of specific implementation details — and I can show you how this might be expressed without using anything as low-level as a `for` loop.
- bryanrasmussen 5y agoI'm not asking help with programming anything, and it seems somewhat insulting that somehow you have managed to turn multiple people's observations that you can't break in a map and so most programmers probably need something more than map or reduce into a request for tutelage - especially when you've spent a lot of time saying the impossible (implementing break in map) was trivial before switching to observing it was not sensible to do - which I take as meaning an admission of the aforementioned impossibility. so to clarify - I am not asking for your help in solving my programming problems. You made a response to another poster asserting that >all most programmers need is either `map` or `reduce` that poster said >I find it really difficult to believe that there are developers out there that never need to be able to break or continue in a loop. you said >I’m not sure where you got “never” from in my comment. I, in my normal long-winded way gave you the benefit of the doubt that you meant something else but advised the previous poster had assumed you meant more than 50% of programmers will not ever need other array methods than map or reduce and hence was confused because you cannot break or continue with those (with map and reduce together you can implement something that has the same end effect as continue but it is not 100% the same of course) but for some reason you just wanted to keep saying you were right and others were wrong even though the clear meaning of everyone's text was - you can't break in a map. And you said: >Regardless of how you choose to interpret my comment, it is the case that both `break` and `continue` are literally never necessary, as those two constructs can be implemented in terms of `map` and `reduce`. frankly exasperated I said >ok, how do you implement a break of the iteration in map in JavaScript? I believe it cannot be done, you believe it can? Teach me. now admittedly here you may think I am confused and need help, but really I am saying you are wrong in a nice way when you say break can be implemented in map. But then you say: >There is no sensible way to `break` the iteration while mapping. That's not what `map` is for. right, what everyone's been telling you from the beginning. >Let me know what it is you're trying to achieve — at a high level, not in terms of specific implementation details — and I can show you how this might be expressed Thank you for your kindness but it should be obvious from the whole conversation nobody is asking you for programming help here, they are just saying your initial statement of most programmers only need map and reduce was overly bold. As to why someone might want to break a loop, generally you do that when you have a long array - say 10000 items (don't bother telling me that 10000 is not a long array, I know, but a long array is often used as meaning an array requiring a lot of processing and that is partially determined by what you need to do with that array, if that is not good enough for you arbitrarily add 0s to the array length until you feel you have a long array) and have a number of conditions that can cause you to only have to process a number of them which probably in that case you would use find (because often in such a case you are processing an array to find one item in it) or if for some reason you needed to drop down to a lower level I would prefer to use while instead of a for loop because semantically I think while indicates to anyone reading the code without going into the loop - hey Bryan doesn't expect to have to look at every element of this array! BUT ALL THAT DOESN'T MATTER - because the subject matter of this long discussion was you saying most programmers only need map and reduce and someone asked why do you think most programmers will NEVER need to break. And you wanted to know why they assumed that you thought most programmers would NEVER need to break because evidently you felt you never said anything remotely like that.
- nefitty 5y agoI basically rely on map 80% of the time for iteration. I do have to spend some mental cycles on deciding where to go if I needed to break or skip elements. That was until I discovered the beautiful .flatMap() trick of returning an empty array to skip or exclude an index item. Is it bad to use map?
- yakshaving_jgt 5y agoWhat are you struggling with when trying to skip elements? > Is it bad to use map? No.
- nefitty 5y agoWhether to use a for loop or for of or forEach or map().filter() etc flatMap takes care of a lot of that
- yakshaving_jgt 5y agoWhy are you using a “trick” to skip an element in some structure when mapping a function over it? For posterity (and forgive me if this JavaScript syntax is inaccurate; I don't write it so much these days)… xs.map(a => shouldBeSkipped ? a : f(a))
- wlamartin 5y agoThey don't want to keep the unmapped value in the resulting collection at all. flatMap allows for removal of an element in one traverse, unlike filter+map with eager behaviour.
- yakshaving_jgt 5y agoThen why not just use a fold?
- wlamartin 5y ago
- ReleaseCandidat 5y ago> Actually, all most programmers need is either `map` or `reduce`. You actually don't need `map`, you can easily implement that using `reduce` too ;)
- yakshaving_jgt 5y agoI'm well aware of that, but I don't think that's a good idea. There is utility in communicating whether you're working with a catamorphism vs a homomorphism. Code is written for humans, after all.
- ReleaseCandidat 5y agoOf course, that wasn't meant seriously. That's why i like `for_each` (or `iter` or whatever it's called) as 'third' function too, because it makes it clear you're doing side-effects.
- feupan 5y agoNobody needs reduce, you’re just making code make less sense. If you’re using reduce when map works you’re just a masochist. https://twitter.com/jaffathecake/status/1213077702300852224 https://twitter.com/jaffathecake/status/1213077702300852224
- brundolf 5y agoClassic for-loops create state and ask you to manually manage control-flow using an arbitrary conditional expression. They open the door for a whole lot of subtle mistakes that for..of can protect you from in the cases where it applies (which in my experience, are 95% of the cases where you’d use a for loop at all)
- pjmlp 5y agoSo basically only Miranda and Haskell are functional.
- ReleaseCandidat 5y agoHaskell has `forM_` (in Control.Monad), so no luck :(
- oats 5y agoThat's still functional, monads aren't impure or anything :)
- geokon 5y agoWhile I agree on the side-effect side of things, you really shouldn't use a classic for loop when forEach suffices. forEach emphasizes you are operating on each element of the loop in a semi-independent fashion and you explicitly don't have access to any kind of intermediary index/iterator variable. When you "drop down" to a classic for loop you're telling the reader that you need access to the index/iterator for some reason (and there are plenty of valid reasons for that). If you only use classic for loops then the reader has to be constantly vigilant you're not doing any index/iterator shenanigans. It's just higher "cognitive load"
- ablu 5y agoI think `for element of list` nowadays starts to become a realistic choice (without a transpiler) for that: https://caniuse.com/?search=for...of https://caniuse.com/?search=for...of
- cehrlich 5y agoAs an added benefit, `for...of` works with async, unlike forEach.
- dragonwriter 5y agoIf I would be tempted to use forEach but can’t because async, I usually don’t want for…of semantics, though. I probably want something like await Promise.all(collection.map(thing => some_async_function(thing))); instead of: for thing of collection { await some_async_function(thing); }
- mnahkies 5y agoThe index and the collection iterated over is passed as the 2nd/3rd parameters in JavaScript, so technically you still have access to them, though in the majority of cases people will omit these parameters
- dragonwriter 5y ago
- gear54rus 5y agofor is awkward in JS because you get either keys or values and not both ForEach does get you both. I don't remember using for loop for at least 3 years previously as a frontend engineer :)
- feupan 5y agofor (const [index, value] of array.entries()) {} It is longer than: array.forEach((value, index) => {}) … but forEach has too many limitations (no exits, no await, …)
- gear54rus 5y agooh yeah, destructuring's nice I'll give you await (gotta use awkward reduce there) but if you want exit, you're better off with something like `find` Another advantage is that array functions better convey what are you trying to do with this particular operation.
- ReleaseCandidat 5y ago> IMO it's the worst of both worlds and there's virtually never a good reason to use it. I strongly disagree.
- lloydatkinson 5y agoThe airbnb style guide bans usage of forEach too for the same reasons
- deleted 5y ago[deleted]
- fulafel 5y agomap() is great in most languages but JS manages to WTF it: > ['1', '2', '3'].map(parseInt) [1, NaN, NaN] > ['3', '2', '1'].map(parseInt) [3, NaN, 1]
- brundolf 5y agoThis problem isn’t about map(). parseInt takes a second argument - a radix - and map passes a second argument to to the callback: an index The normal solution is to write an inline function; “shortcutting” it by just passing a named function directly can be nice when it’s appropriate, but it often isn’t what you want to do
- fulafel 5y agoComing from outside the JS world, the problem is exactly about surprising behaviour of map - the function that's probably the oldest and best estabilished element of FP vocabulary. The extra index argument is not present in functions called "map" in other languages. Also, the silently ignored argument count mismatch errors heavily contribute to the problem. You may have used other functions with map() and never knew it's always passing an extra argument in, because JS silently ignores this error.
- brundolf 5y agoSorry if it’s unfamiliar, but the index argument is critical to many real use-cases. As far as ignoring errors silently, sure, but I’m not even sure what the error would be in this case. Index is a number and radix is a number; even typescript won’t complain about this because the types line up.
- fulafel 5y agoIs it critical in some way that's absent from all the other langugaes that have a map()? There are standard, composable, explicit patterns in Python, Clojure, and I presume other map()-equipped anguages to get you the indices along with the items. (enumerate in Python and map-indexed in Clojure) The error I was talking about was the argument count mismatch erorr, that would have taught you on all the other times you used map() without knowing about the extra index argument.
- throwawa923423 5y agoI get where you are coming from, but it is obvious to me `forEach` is side effect because of its return type. Also I think `forEach` is great for cases such as `arr.forEach(f)` where `arr: Array<T>, f: T => void`.