Do we want `forEach`?

Content warning: heavy editorializing

Speaking personally: forEach is a blight on Sequence and I wish it didn’t exist. People use it arbitrarily instead of for…in for no apparent good reason. Frequently the answer they give for why is “it’s swifty” or “it’s more functional”. No. Just use a for loop.

forEach certainly has its uses. I believe the original intent was to stick on the end of a long composed chain of operations i.e. myArray.lazy.map(foo).filter(bar).forEach. This is reasonable… except often these long chains are a bad idea. Less experienced developers see them in code, try to replicate them, and then get tied up in knots with type mismatches. It would be far better for them to break the code up and see the individual types, but they can’t because of the chain. Maybe tooling could fix this but… does it need fixing? “Doctor doctor, it hurts when I compose too many things”. Well, don’t compose so much.

The main use I reach for in forEach is debugging… it would be nice sometimes to be able to stick .forEach(print) on the end of an expression. Unfortunately print has default arguments which don’t work with point free style so you have to write { print($0) } which kind of spoils the fun.

I think there’s another reason why forEach on async sequence specifically isn’t a great idea and that’s that it obscures the awaiting done by it. Unlike with synchronous sequences you may find execution paused for a long time awaiting the next element, and a proper for loop will make what is going on clearer in the debugger.

Anyway in conclusion sorry for the rant and please don’t add it, deprecate the one on Sequence instead (this will not happen but I can dream).

52 Likes