3 ms·
> 1) The automatic parallelization opportunities of HOFs don't happen in practice nearly as often as you'd expect. Sorry, but it's true. Haskell's "map" doesn't
by DannoHung 11y ago
> 1) The automatic parallelization opportunities of HOFs don't happen in practice nearly as often as you'd expect. Sorry, but it's true. Haskell's "map" doesn't parallelize automatically.
Haskell generally won't parallelize unless you tell it to. HOFs are not specifically for parallelization, however the cost associated with switching from a serial HOF to a parallel HOF can be very, very low. The same is absolutely not true for loops. There are some interesting things available with SIMD macros, but that's not parallelization in the way you're referring to it.
> 2) Loops are a single construct you learn once and apply everywhere, while HOFs are a huge and bewildering zoo.
This is sort of true, but as noted in a sibling comment, this is not necessarily a good thing. Each loop construct can behave differently. The invariant could change, the initial index might be different, the increment is arbitrary. None of those are things you are supposed to do, but they are all stepps taken used to obtain a behavior. When you are using HOF's, each one has a specific purpose and behavior and they can be understood in isolation and then in as many or few compositions as desired
> 3) Loops can do many things that HOFs cannot. You can do break/continue/return in the loop body. You can iterate over two collections at once, skipping elements of one collection depending on what you see in the other. And so on.
I'll agree on this point, mostly. They are strictly more powerful when you want to do something complicated. You do pay for the power with complexity. My one quibble is that it isn't necessarily that hard to do co-iteration, and let's be honest, most co-iteration is done 1 for 1.
> 4) Loops scale to from simple use cases to complicated ones gradually and continuously. With HOFs, when your use case changes slightly (e.g. you add a counter), you often need to go all the way up and use a different HOF.
I don't see this as even a minor issue. In fact, it's kind of the point. If you need to do something different, create a new composition of functions and as long as they have the same type signatures and the operation is what you now want, there's no problem at all.
> 5) Loops make the time and space complexity much more obvious. With HOFs, you often get nasty surprises. For example, see the tail-recursive vs non-tail-recursive implementations of "map" in OCaml, or the foldr vs foldl vs foldl' situation in Haskell.
Except when they don't, as in the case where you are doing manipulations of the index or loop invariants; one of the things you cited as a benefit of loops.
I don't know about OCaml, but foldr and foldl are strictly different behaviors. One folds from the left, the other folds from the right, which is seriously important if you're using a non-associative operation. Now, maybe you want to say this is backing your point up, but write a for loop that does a non-associative left fold over a single linked list and we'll talk about obvious complexity.
To make an aside, are you maybe thinking about this in terms of Haskell's lists vs C arrays? The fact that the data structures are so, so different makes a huge impact on how you work with them.
foldl vs foldl' has nothing to do with HOF in particular, but Haskell's non-strict evaluation semantics. If you want to argue about strictness vs non-strictness though, that's a totally valid thing where I don't think there's any single dominant viewpoint. Non-strictness has certain benefits, but it's not necessarily worth it, especially if you can ask for limited non-strictness with otherwise strict semantics.
6) Loops are much easier to understand from a machine point of view. For HOFs, you need a language with closures, and in some cases GC as well. Loops, on the other hand, can be done in C.
You don't need a language with closures. Closures are definitely a great thing to have in general, but all you need is first class functions. You can write HOF's in C if you want to. It's a pain in the ass because function pointers are annoying as all hell, but you can absolutely do it.
Overall, I'll say that there's probably a point to be made that there's a bit of overhead in learning all the different and varied HOF's that work with a function, but I don't really think it's that much larger than becoming familiar with any given pattern you can implement in a loop construct. The difference being that you implement the loop construct every time you want to use it. With a HOF, you just refer to the one you want to use.