To some extent for while loops are a matter of taste. But the issue is that unless you have mutable variables, you cannot use typical imperative control variables.
So something like:
bool done = false;
while (!done) {
...
}
makes no sense unless you have some way of setting `done = true`; which is not possible in purely functional languages like Haskell.
Functional languages support this type of operation in a few different ways.
One is through constructs like Clojure's `take-while`. Instead of setting a vairable when you want to exit the loop, you define a predicate that is false when the loop should exit.
Another is through `any` and `all` (aka `some` and `every`), which work in a similar way to `take-while`, except they reduce OR or AND across the returned values from the predicate as they go.
This construct is totally possible in Haskell, one common way of achieving it is to store the 'done' boolean in a State monad.
The really funny thing is that the while loop itself is not built-in to Haskell but you can write it yourself as a function. In other languages (non-lazy) this sort of thing is not possible without resorting to macros or other tricks, because laziness is required in order to define the correct semantics for conditionals.
functional programmers are kind of like cargo bike riders. they know they can solve their problem otherwise and in a much simpler fashion but insist on riding the cargo bike.
Looping was more natural to me, but learning Elixir (I've been using Learn Functional Programming with Elixir) made it so much easier to grasp and more natural. It's a fun language to use to boot.
It's a very clear infinite recursion at best, and a buggy definition for odd `n` if that's fixed. I wouldn't imagine making either error if just looping...
The concept of a loop (i.e. doing something over and over as long as a condition is true) is (again IMHO) a much simpler concept than recursion.
The university I worked at tried moving functional programming into the first semester. Let's just say, that didn't work out at all. The imperative programming style is apparently much easier for students to grasp.
Edit: The more I think about it, it seems to me that a loop is clearer because the initial state, upon which you iterate on, is much more obvious. Where with recursion, I primarily see the step, but not where exactly I started from -- if that makes any sense..
is kind of clearer. But quite often it is not that easy to give an recursive solution for an iterative problem. When you want to change every even number in an array/list from n to n+1 you can somehow do it recursively, but it is mor obvious to iterate over the elements (which in many functional languages can be expressed by "apply" or some similar function).
I don’t think that’s much clearer at all. I realize that you’re citing a specific example, but if your approach is to understand the problem first, then you may as well use the closed-form solution of your problem:
sum(1,n) = n*(n+1)/2
Which some compilers will produce for you, even if you use loops.
Maybe if you chose a different example, like generating power sets or the Fibonacci sequence, your point would be better made?
I can see why I never got very far with functional programming, it's much too clever for me. I don't see how this example has any sort of limit test in it at all much less stops at or rather defines an extent of 5,000. oh well imperative peasant life can be a drudge but it gets my corn sown one kernal at a time.
Be very careful with "why do X hate Y?" Generally speaking, if someone comes out as outright hating Y, their opinion should be taken with a pound of salt.
Functional programming is fundamentally at odds with imperative constructs. It's not that people hate them, it's that they generally just don't fit the paradigm of "build a pipeline of steps (functions) and feed data into it" as well. Most of my software is functional, but you will find the occasional C-style loop, especially if the map/filter/reduce alternative is ridiculously complicated to read.
Yeah for sure! The tricky, prickly part of talking about FP is that we get into semantics a lot (eg. I'm sure there's Haskell experts who wouldn't consider JavaScript's `Array.prototype` functions to count as FP). Generally I'm thinking about `foo.map` vs. `for n in foo...`. Maybe "imperative" is a bit of a loaded term for this point.
I find concat pipelines to become unwieldy very quickly. I’ll generally turn to an imperative loop when my output doesn’t fit neatly into an FP paradigm, or requires a ridiculous amount of translations to accomplish. At the end of the day, I still want to write performant code.
Loops are a general purpose device that must be specialized for a given use case - e.g. map, reduce, fold et cetera. Programmers often get the details wrong when implementing such use cases. In a functional program, each use case is named and generally available (in a correct form) via a library or built-in operation.
The functional programmers (in my circle) have latched on to forEach/map/reduce/filter etc as the bibile of functional programming. Writing a simple for..each loop will make them reject PRs
forEach is just an alternate syntax for a for..in loop, that instead of being a block it takes a closure. It's not functional, since it's only used for side-effects.
Loops have the potential to be very inefficient. As an exercise you can write two scripts in Javascript, one that has a for loop and another that does the same thing functionally. Then you can use Node to dump the VM instructions. You might be surprised at how many fewer instructions the functional version requires.
I had thought tail-call optimization was a way to get inefficient recursion to parity with iteration, by turning it into iteration. I'd never heard of recursion being faster on its own.
Is it a javascript-specific thing due to how their int types work?
In von Neumann architectures, iteration will always be faster than recursion, because it'll need way less machine code. 2 caveats:
1rst one: It really depends on your data structure. If you use a tree, recursion will be faster on most operations (unless you optimize like crazy, but tbh, it's never worth it). Even with simple linked lists, some operations are better written with recursion.
2nd: more often than not, using first order functions will be both more efficient and easier to read than a for loop. If you're using a foreach loop to apply a function to each items, please use a map.
That is just untrue. Compilers tend to generate better code for imperative algorithms because current architectures implement a fundamentally imperative model. Yes, "sufficiently advanced" compilers can generate code that has similar performance to imperative algorithms from functional ones. But that requires some heavy lifting static analysis and optimisation passes. Have a look here: https://godbolt.org/z/sPT3snWE4. Only if you remove `#[inline(never)]` from `add1` will you get efficient code for the functional algorithm. This is a trivial example, but it ilustrates some of the nuances.
most functional techniques wrap walking a loop into a few functions, like fold. writing loop constructs can be error prone. one is much less liable to make a mistake passing in an array into a function and have it processed using another function. idea being separation of logic. why care about loops when one only thinks in arrays and streams being processed? i don't think i've written a for loop in js in a couple years at this point with the help of libraries like ramda.
In theory, purely functional programming is declarative in nature - there are things you can tell about the state of the program just from reading the code, whereas in imperative programming (with loops and changing state) you can't tell with confidence what the state of the program will be without running it. There's a certain appeal to the declarative flavour that I used to care a lot about and somehow managed to forget to care about. FWIW I can't say that my programs became worse when I forgot to care about purity. But there may be some scenarios and domains where this sort of confidence can make a difference, and I just happen to not work often in these areas.
I think one problem is that some of the syntax doesn't express what you are trying to do, it's just low level mechanism, especially C for loops for example.
But going further, many loops are actually maps, where you do something independently with each element of a list. Why formulate that as a linear visit to each element?
Looping constructs - How it is done.
Functional - What is done.
The latter is declarative and more expressive and when you compose these pieces, you express your code at a higher level of abstraction as a whole. Maintainability, fewer scope for bugs etc.
1. Immutability: Functions are expressions that return a singular value without mutating state. Most loops are not expressions and have some mutation happening.
2. Composition: If the loops don't mutate there are functional alternatives (map, reduce/fold, filter, etc) which are composable.
3. Abstraction: Loops are traversal instruments. Sophisticated functional languages / environments have some mechanism for generalizing traversal of any data structure into a higher order function or some form of reusable generalization.
That said, I use a lot of loops in library code for performance and expose functional interfaces for consumers.
```
let parent = document.createElement("div")
for (let user of users) {
let userElem = renderUser(user)
parent.appendChild(userElem)
}
return parent
```
vs
```
let parent = document.createElement("div")
parent.appendChild(...users.map(renderUser))
```
vs
```
<div>
{users.map(user => <User {user} />)}
</div>
```
It's kind of silly IMHO. Recursion has plenty of "state", it's added on the stack before each function call. So under the hood it's all stateful. But instead of e.g. a nice simple counter variable, you've got an ever growing pile of stack frames - with hopefully some compiler shenanigans to eliminate them and convert it to a counter internally.
Functional languages use recursion instead of for and while loops. Recursion is a more general mechanism that can be used to implement the equivalent of for and while loops.
So in short, functional programmers do not "hate" looping construct, but simply don't need them since you can use recursion instead (in practice you actually rarely use recursion directly, but instead use functions like map, fold, sum, etc. which are implemented recursively).
The point is not about ignoring them; the point is about using a construction with higher semantics.
A loop is a label+goto in disguise. You have to look at what's inside to understand what the loop is doing; you can't get a global sense of how it is impacting data without looking at what it actually does.
In functional programming, loops are replaced by constructs such as foreach, reduce, map or filter, to indicate right away how the body of the loop operate on the data it is scanning. It enables a functional approach to looping, where one first thinks about how data are transformed -- about the functions to apply on items and what is the overall output of the loop.
The rule of thumb I use is if something has side effects, I use a for loop. If it doesn't, I use whatever functional constructs the language gives me. They're obviously equivalent approaches, but the choice provides some semantics to people reading the code: look carefully at for loops for side effects.
What's interesting is that you start to see less need for side effects in most cases. Still some, but a lot less than you'd think.
It's the fundamental difference between declarative and imperative programming.
Loops conflate two different questions: "what do you want?" and "how do you want it done?" If I write (Haskell):
map (+1) list_of_numbers
I have expressed only that I want one added to every number in a list. Whereas if I write (C):
for (size_t i = 0; i < sizeof(array_of_numbers); i++) array_of_numbers[i]++;
I have expressed both that I want one added to every number in the array and I have also described exactly how to do it, including the order I want it done in.
So what's the big deal? On the one hand, the imperative approach gives me more control. On the other hand, I may not want that control! Perhaps I don't care what order the numbers are incremented in, I just want it done, and the extra details in the for loop are just "line noise" which gets in the way of being able to quickly read the code and understand what it does.
There are other factors, of course. One advantage to the functional approach is that the library author who supplies map (since it's not built-in to the language) is free to change the details under the covers. Perhaps they find a clever way to parallelize the implementation, then all of my code which uses map gets a free speed-up! Now, there may be other reasons why this won't work in practice (to do with legacy code, of course) but the principle is sound!
You are too the point. With the nitpicky addition, that Functional Programming prefers things like looping as functions to generate a homogenous construct which you can play with. There is no reason why loop cannot be expressed with some keywords and focus on the WHAT and hiding the HOW.
72 comments
[ 4.4 ms ] story [ 123 ms ] threadSo something like:
makes no sense unless you have some way of setting `done = true`; which is not possible in purely functional languages like Haskell.One is through constructs like Clojure's `take-while`. Instead of setting a vairable when you want to exit the loop, you define a predicate that is false when the loop should exit.
Another is through `any` and `all` (aka `some` and `every`), which work in a similar way to `take-while`, except they reduce OR or AND across the returned values from the predicate as they go.
EDIT: grammar and clarity
The really funny thing is that the while loop itself is not built-in to Haskell but you can write it yourself as a function. In other languages (non-lazy) this sort of thing is not possible without resorting to macros or other tricks, because laziness is required in order to define the correct semantics for conditionals.
Cargo bikes don't get stuck in traffic; functional programmers don't like getting stuck debugging.
Cargo bikes are easy to park; functional programs are easy to run in a REPL.
...and so on.
Nothing beats fitting a week of groceries on your bike, though.
Instead of looping from 1 to 5000, you define the relationship instead:
sum(1,n) = 1 + n + sum(2, n-1).
Isn't this clearer to understand problem first, instead of just looping ?
some people think in both. they should write blogs and help the whats and the hows communicate.
The concept of a loop (i.e. doing something over and over as long as a condition is true) is (again IMHO) a much simpler concept than recursion.
The university I worked at tried moving functional programming into the first semester. Let's just say, that didn't work out at all. The imperative programming style is apparently much easier for students to grasp.
Edit: The more I think about it, it seems to me that a loop is clearer because the initial state, upon which you iterate on, is much more obvious. Where with recursion, I primarily see the step, but not where exactly I started from -- if that makes any sense..
Instead of looping from 1 to 5000, you compute (1 + 5000) * 5000 / 2
This is what "understand problem first" actually means
sum(1,n) = n*(n+1)/2
Which some compilers will produce for you, even if you use loops.
Maybe if you chose a different example, like generating power sets or the Fibonacci sequence, your point would be better made?
The former prefers a "declaration" of THE state. for/while loops imply a change of state, so they're a big no here.
The latter prefers an "imperative set of steps" to go from initial state to the desired state. for/while loops make the "steps" easier here.
Functional programming is fundamentally at odds with imperative constructs. It's not that people hate them, it's that they generally just don't fit the paradigm of "build a pipeline of steps (functions) and feed data into it" as well. Most of my software is functional, but you will find the occasional C-style loop, especially if the map/filter/reduce alternative is ridiculously complicated to read.
Arrows in Haskell are actually pretty good at building functional left to right data processing pipelines.
---
[1] https://en.wikibooks.org/wiki/Haskell/Understanding_arrows
I had thought tail-call optimization was a way to get inefficient recursion to parity with iteration, by turning it into iteration. I'd never heard of recursion being faster on its own.
Is it a javascript-specific thing due to how their int types work?
1rst one: It really depends on your data structure. If you use a tree, recursion will be faster on most operations (unless you optimize like crazy, but tbh, it's never worth it). Even with simple linked lists, some operations are better written with recursion.
2nd: more often than not, using first order functions will be both more efficient and easier to read than a for loop. If you're using a foreach loop to apply a function to each items, please use a map.
There are many religions in programming world
Just get familiar with them, take what is sane and avoid the rest
Extremas are rarely the best
Well, they are either the best or the worst (≧▽≦)
Once these came naturally, doing anything else starts to feel underconstrained and imprecise.
oh and loops constructs are not composable...
But going further, many loops are actually maps, where you do something independently with each element of a list. Why formulate that as a linear visit to each element?
The latter is declarative and more expressive and when you compose these pieces, you express your code at a higher level of abstraction as a whole. Maintainability, fewer scope for bugs etc.
2. Composition: If the loops don't mutate there are functional alternatives (map, reduce/fold, filter, etc) which are composable.
3. Abstraction: Loops are traversal instruments. Sophisticated functional languages / environments have some mechanism for generalizing traversal of any data structure into a higher order function or some form of reusable generalization.
That said, I use a lot of loops in library code for performance and expose functional interfaces for consumers.
It's about what conveys the intent most clearly.
What do you prefer?
``` let sum = 0; for (let elem of array) { sum += elem.value } return sum ```
Or
``` Math.sum(...array.map(elem => elem.value)) ```
What about
``` let parent = document.createElement("div") for (let user of users) { let userElem = renderUser(user) parent.appendChild(userElem) } return parent ```
vs
``` let parent = document.createElement("div") parent.appendChild(...users.map(renderUser)) ``` vs ``` <div> {users.map(user => <User {user} />)} </div> ```
The usual way to turn them into smaller problems is induction and the easiest way to turn inductive reasoning into code is to write recursive code
So in short, functional programmers do not "hate" looping construct, but simply don't need them since you can use recursion instead (in practice you actually rarely use recursion directly, but instead use functions like map, fold, sum, etc. which are implemented recursively).
A loop is a label+goto in disguise. You have to look at what's inside to understand what the loop is doing; you can't get a global sense of how it is impacting data without looking at what it actually does.
In functional programming, loops are replaced by constructs such as foreach, reduce, map or filter, to indicate right away how the body of the loop operate on the data it is scanning. It enables a functional approach to looping, where one first thinks about how data are transformed -- about the functions to apply on items and what is the overall output of the loop.
What's interesting is that you start to see less need for side effects in most cases. Still some, but a lot less than you'd think.
Loops conflate two different questions: "what do you want?" and "how do you want it done?" If I write (Haskell):
I have expressed only that I want one added to every number in a list. Whereas if I write (C): I have expressed both that I want one added to every number in the array and I have also described exactly how to do it, including the order I want it done in.So what's the big deal? On the one hand, the imperative approach gives me more control. On the other hand, I may not want that control! Perhaps I don't care what order the numbers are incremented in, I just want it done, and the extra details in the for loop are just "line noise" which gets in the way of being able to quickly read the code and understand what it does.
There are other factors, of course. One advantage to the functional approach is that the library author who supplies map (since it's not built-in to the language) is free to change the details under the covers. Perhaps they find a clever way to parallelize the implementation, then all of my code which uses map gets a free speed-up! Now, there may be other reasons why this won't work in practice (to do with legacy code, of course) but the principle is sound!