TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Ask HN: Why do functional programmers hate loops (for, while, etc.)?

27 pointsby newsoulalmost 2 years ago
Why do functional programmers hate looping constructs of imperative languages and ignore them as if they don't exist?

36 comments

chonglialmost 2 years ago
It&#x27;s the fundamental difference between declarative and imperative programming.<p>Loops conflate two different questions: &quot;what do you want?&quot; and &quot;how do you want it done?&quot; If I write (Haskell):<p><pre><code> map (+1) list_of_numbers </code></pre> I have expressed only that I want one added to every number in a list. Whereas if I write (C):<p><pre><code> for (size_t i = 0; i &lt; sizeof(array_of_numbers); i++) array_of_numbers[i]++; </code></pre> I have expressed both that I want one added to every number in the array and I have also described exactly <i>how</i> to do it, including the order I want it done in.<p>So what&#x27;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&#x27;t care what order the numbers are incremented in, I just want it done, and the extra details in the for loop are just &quot;line noise&quot; which gets in the way of being able to quickly read the code and understand what it does.<p>There are other factors, of course. One advantage to the functional approach is that the library author who supplies map (since it&#x27;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&#x27;t work in practice (to do with legacy code, of course) but the principle is sound!
评论 #36681601 未加载
pipo234almost 2 years ago
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 <i>control variables</i>.<p>So something like:<p><pre><code> bool done = false; while (!done) { ... } </code></pre> makes no sense unless you have some way of setting `done = true`; which is not possible in purely functional languages like Haskell.
评论 #36681397 未加载
评论 #36681289 未加载
Waterluvianalmost 2 years ago
Be very careful with &quot;why do X hate Y?&quot; Generally speaking, if someone comes out as outright hating Y, their opinion should be taken with a pound of salt.<p>Functional programming is fundamentally at odds with imperative constructs. It&#x27;s not that people hate them, it&#x27;s that they generally just don&#x27;t fit the paradigm of &quot;build a pipeline of steps (functions) and feed data into it&quot; as well. Most of my software is functional, but you will find the occasional C-style loop, especially if the map&#x2F;filter&#x2F;reduce alternative is ridiculously complicated to read.
评论 #36681312 未加载
评论 #36681035 未加载
intellectronicaalmost 2 years ago
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&#x27;t tell with confidence what the state of the program will be without running it. There&#x27;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&#x27;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.
Noe2097almost 2 years ago
The point is not about ignoring them; the point is about using a construction with higher semantics.<p>A loop is a label+goto in disguise. You have to look at what&#x27;s inside to understand what the loop is doing; you can&#x27;t get a global sense of how it is impacting data without looking at what it actually does.<p>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.
weatherlightalmost 2 years ago
In functional programming, we value immutability and the notion of pure functions, meaning their outputs depend solely on their inputs without any side-effects. This is one of the reasons why we often lean away from traditional loop constructs of imperative languages, which are typically stateful and can produce side effects.<p>Instead of using loops, we often use higher-level functions like map, filter, and reduce. These functions allow us to focus more on the &quot;what&quot; (the operation being performed) rather than the &quot;how&quot; (the control flow). It&#x27;s like saying, &quot;transform each item in this list,&quot; as opposed to, &quot;start here, do this to each item until you get to the end.&quot;<p>And when we do need to perform repetitive operations, we tend to use recursion rather than loops. Recursive solutions don&#x27;t require mutable state and are easier to reason about, especially when the logic gets complex.<p>Lastly, mutable state, which is common in loops, can lead to issues such as race conditions in multithreading or distributed computation environments. The minimized state changes in functional programming makes it often more suitable for these scenarios.<p>So, it&#x27;s not really about &quot;hating&quot; loops, but more about choosing the constructs that align better with the philosophy and goals of the functional paradigm.<p>To conclude, In order to use imperative looping constructs, you have to violate referential transparency and immutability, which are the hallmarks of functional programming.<p><a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Referential_transparency?useskin=vector" rel="nofollow noreferrer">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Referential_transparency?usesk...</a><p><a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;w&#x2F;index.php?title=Immutable_object&amp;useskin=vector" rel="nofollow noreferrer">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;w&#x2F;index.php?title=Immutable_object&amp;...</a>
behnamohalmost 2 years ago
For some people, thinking in &quot;functions&quot; makes more sense than thinking in &quot;algorithms&quot;.<p>The former prefers a &quot;declaration&quot; of THE state. for&#x2F;while loops imply a change of state, so they&#x27;re a big no here.<p>The latter prefers an &quot;imperative set of steps&quot; to go from initial state to the desired state. for&#x2F;while loops make the &quot;steps&quot; easier here.
woudsmaalmost 2 years ago
In JS, functions like filter&#x2F;map&#x2F;reduce can help when you try to write immutable code because you only work with the arguments, and return an output value. You don&#x27;t have to define an empty&#x2F;temporary array first, and fill it up in your regular for-loop for example.<p>I don&#x27;t &#x27;hate&#x27; loops, I still use them sometimes. Personally I just try to avoid them (in JS) because I feel like I can solve my problem without any side-effects. It&#x27;s something less to think about. It&#x27;s nice when the logic&#x2F;variables for a function are encapsulated entirely inside that function. It also makes it easy to extract functions so you can re-use them elsewhere.
nvlnalmost 2 years ago
1. Immutability: Functions are expressions that return a singular value without mutating state. Most loops are not expressions and have some mutation happening.<p>2. Composition: If the loops don&#x27;t mutate there are functional alternatives (map, reduce&#x2F;fold, filter, etc) which are composable.<p>3. Abstraction: Loops are traversal instruments. Sophisticated functional languages &#x2F; environments have some mechanism for generalizing traversal of any data structure into a higher order function or some form of reusable generalization.<p>That said, I use a lot of loops in library code for performance and expose functional interfaces for consumers.
cocodillalmost 2 years ago
I guess looping constructs have some kind of states and functional paradigm, as I understand it, avoids them.
EnergyAmyalmost 2 years ago
The rule of thumb I use is if something has side effects, I use a for loop. If it doesn&#x27;t, I use whatever functional constructs the language gives me. They&#x27;re obviously equivalent approaches, but the choice provides some semantics to people reading the code: look carefully at for loops for side effects.<p>What&#x27;s interesting is that you start to see less need for side effects in most cases. Still some, but a lot less than you&#x27;d think.
QuadrupleAalmost 2 years ago
It&#x27;s kind of silly IMHO. Recursion has plenty of &quot;state&quot;, it&#x27;s added on the stack before each function call. So under the hood it&#x27;s all stateful. But instead of e.g. a nice simple counter variable, you&#x27;ve got an ever growing pile of stack frames - with hopefully some compiler shenanigans to eliminate them and convert it to a counter internally.
评论 #36681348 未加载
noelwelshalmost 2 years ago
There are many reasons. Here&#x27;s one.<p>FP is built on theory. The language constructs follow the theory. Part of the theory is algebraic data types which allow for structural recursion. Fancy words that basically come to mean the structure of the code that manipulates data follows the structure of the data it manipulates.<p>Take a loop typical loop<p><pre><code> var someResult = 0; for(i = 0; i &lt; whatever; i++) { someResult := updateResult(i, someResult); } </code></pre> This uses the natural numbers (integers 0, 1, 2, ...) [Some people define the naturals to start at 1. This isn&#x27;t important.]<p>The natural numbers are an algebraic data type and therefore allows for structural recursion. For the natural numbers the structure is, a natural number N is either:<p>* 0; or<p>* 1 + M, where M is a natural number<p>So you write out your structural recursion skeleton<p><pre><code> def loop(n) = n match { case 0 =&gt; case n =&gt; loop(n - 1) } </code></pre> This exactly follows the structure of the data, including the recursion.<p>Then you fill in the missing pieces<p><pre><code> def loop(n) = n match { case 0 =&gt; 0 case n =&gt; updateResult(n, loop(n - 1)) } </code></pre> Job done. The theory guides us to the solution. No loop needed.<p>A lot more here: <a href="https:&#x2F;&#x2F;www.creativescala.org&#x2F;creative-scala&#x2F;recursion&#x2F;" rel="nofollow noreferrer">https:&#x2F;&#x2F;www.creativescala.org&#x2F;creative-scala&#x2F;recursion&#x2F;</a><p>The main other reason is equational reasoning aka reasoning using substitution. Described here: <a href="https:&#x2F;&#x2F;www.creativescala.org&#x2F;creative-scala&#x2F;substitution&#x2F;" rel="nofollow noreferrer">https:&#x2F;&#x2F;www.creativescala.org&#x2F;creative-scala&#x2F;substitution&#x2F;</a>
BenoitPalmost 2 years ago
Loops have an implicit ordering, and thus are inherently non parallelizable at face value. map can enable easy parallelization, wether with SIMD or threads or GPU warps or Spark nodes. It&#x27;s just a construct that specifies less about execution, and thus leaves more room for the compiler&#x2F;runtime.<p>This construct is natural with functional programming&#x27;s abstraction: the function. You encapsulate basic behavior in functions, and then shape the data flow with map(fn)&#x2F;reduce()&#x2F;flatmap(). If you really need an index, you can still have them with number ranges, zipWithIndex, etc. If you do use them, you are explicitly reintroducing ordering into your program and lose the parallelism.<p>This is just a finer level of detail wrt accidental vs essential complexity. IMHO it is always better to specify less about your program, and stick to what you must encode and only that.
willmiller_almost 2 years ago
Have you used tail|head in any language. To them it seems more elegant and a &quot;better&quot; way of doing things.
Jeff_Brownalmost 2 years ago
Writing what could be a loop in functional style instead allows you to say what you want done without saying how to do it. The results is faster to write, easier to read, and leaves the compiler free to find a good way to do it (e.g. maybe in parallel).
b0skalmost 2 years ago
Looping constructs - How it is done. Functional - What is done.<p>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.
papaveralmost 2 years ago
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&#x27;t think i&#x27;ve written a for loop in js in a couple years at this point with the help of libraries like ramda.<p>oh and loops constructs are not composable...
marcelralmost 2 years ago
Not all FP programmers hate them, I use them in a pinch.<p>It&#x27;s about what conveys the intent most clearly.<p>What do you prefer?<p>``` let sum = 0; for (let elem of array) { sum += elem.value } return sum ```<p>Or<p>``` Math.sum(...array.map(elem =&gt; elem.value)) ```<p>What about<p>``` let parent = document.createElement(&quot;div&quot;) for (let user of users) { let userElem = renderUser(user) parent.appendChild(userElem) } return parent ```<p>vs<p>``` let parent = document.createElement(&quot;div&quot;) parent.appendChild(...users.map(renderUser)) ``` vs ``` &lt;div&gt; {users.map(user =&gt; &lt;User {user} &#x2F;&gt;)} &lt;&#x2F;div&gt; ```
gwbas1calmost 2 years ago
I think it has a lot more to do with readability. Certain conventions are easier to read. This is especially important as we realize that code is easier to read if we don&#x27;t have to delve into the context.<p>For example, (I&#x27;ve spent a lot of time in C#,) let&#x27;s say you have a collection of Foo objects, but you need to convert them to Bar objects. There&#x27;s two general patterns in C# to do this:<p><pre><code> var bars = new List&lt;Bar&gt;(); foreach (var foo in foos) { bars.Add(foo.ConvertToBar()); } </code></pre> Versus:<p><pre><code> var bars = foos.Select(foo =&gt; foo.ConvertToBar()); </code></pre> Which one is more readable? If you&#x27;re a newcomer to the language, or you generally haven&#x27;t worked with &quot;Select&quot; before, the foreach approach might make more sense.<p>But, as you get used to &quot;Select&quot; and understand what it does, you&#x27;ll probably come to the conclusion that foos.Select is easier to read, especially if you&#x27;re looking at code that someone else wrote, where you might not have all of its context in your head.<p>Some other responses in this thread explain the difference by saying that &quot;loops are saying <i>how</i> you want something done, functional is saying <i>what</i> you want done.&quot; This also is helpful when reading code where you don&#x27;t have all the context in your head. (IE, code someone else wrote, or code you haven&#x27;t worked with in a long time.)<p>To provide an example, you could have an array, int[] in C#:<p><pre><code> var sum = 0; foreach (var i in values) { sum = sum + 1; } </code></pre> Or:<p><pre><code> var sum = values.Sum(); </code></pre> Both are valid C#. But, the functional approach is much easier to read, because you don&#x27;t need to understand as much context when comparing the loop approach to using the Sum() method.<p>If you&#x27;re a novice to C#, a novice programmer in general, or you&#x27;re merely unfamiliar with Linq, the loop approach might seem easier. It&#x27;s &quot;not wrong...&quot; But, remember, you&#x27;re saying &quot;how&quot; instead of &quot;what.&quot;<p>A codebase that constantly says &quot;how&quot; instead of &quot;what&quot; ultimately is harder to work with, because it takes longer for everyone to understand each others&#x27; code.
rkrzralmost 2 years ago
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.<p>So in short, functional programmers do not &quot;hate&quot; looping construct, but simply don&#x27;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).
friend_and_foealmost 2 years ago
It&#x27;s not that they hate loops, it&#x27;s that once you understand recursion and are able to implement it you never want to go back to loops again. Hearing that from others was the main reason I started learning FP, I wanted to see what the hype was all about and let me tell you it&#x27;s absolutely true. Go learn recursion and get a handle on it and you&#x27;ll see why.
icsaalmost 2 years ago
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.
jacknewsalmost 2 years ago
I think one problem is that some of the syntax doesn&#x27;t express what you are trying to do, it&#x27;s just low level mechanism, especially C for loops for example.<p>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?
username332211almost 2 years ago
Problems are generally easier to deal with if you can turn them into smaller problems.<p>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
NoGravitasalmost 2 years ago
Let me offer the one thing that functional programmers and imperative programmers can agree on hating: the Common Lisp LOOP macro.<p>(Which I actually use all the time, if only to save a dependency.)
tomealmost 2 years ago
They don&#x27;t really.<p><pre><code> for [0 .. n-1] $ \i -&gt; ... </code></pre> does pretty much the same thing in Haskell as<p><pre><code> for i in range(0, n): ... </code></pre> does in Python.
grubbypawalmost 2 years ago
Using a map or a reduce instead of a for-loop feels like using a foreach instead of a while-loop.<p>Once these came naturally, doing anything else starts to feel underconstrained and imprecise.
revskillalmost 2 years ago
Well, how to calculate sum from 1 to 5000 ?<p>Instead of looping from 1 to 5000, you define the relationship instead:<p>sum(1,n) = 1 + n + sum(2, n-1).<p>Isn&#x27;t this clearer to understand problem first, instead of just looping ?
评论 #36681012 未加载
评论 #36681017 未加载
评论 #36680985 未加载
评论 #36681799 未加载
评论 #36681157 未加载
评论 #36681039 未加载
评论 #36681125 未加载
评论 #36680961 未加载
评论 #36680981 未加载
评论 #36681267 未加载
评论 #36681047 未加载
brundolfalmost 2 years ago
For a loop to do something useful it has to have side-effects[1], and side-effects are what functional programmers want to avoid<p>[1] At the very least, on variables outside of itself
eksapsyalmost 2 years ago
nobody hates anything. we don&#x27;t hate while&#x2F;for&#x2F;etc loops.<p>We should just be careful when using them just like any other nested structure (if&#x2F;else&#x2F;while-do&#x2F;switch&#x2F;select&#x2F;etc.)<p>Like if I see ``` for ... { for ... { if ... { } else { } &#x2F;&#x2F; code here ... } ... } ```<p>That is objectively bad code and not readable.<p>Nobody hates anything. Most of us just dont like how people misuse them to write bad code because their managers pressure them.
fulafelalmost 2 years ago
I&#x27;d question the premise. When using an imperative language like C, GLSL or Java, FP&#x27;ers do use looping constructs. When in Rome, etc.
Zelphyralmost 2 years ago
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.
评论 #36681041 未加载
评论 #36681287 未加载
hardware2winalmost 2 years ago
Fpers, lispers, tdders, clean coders, etc<p>There are many religions in programming world<p>Just get familiar with them, take what is sane and avoid the rest<p>Extremas are rarely the best
评论 #36681527 未加载
nullcipheralmost 2 years ago
Ah, i hate those coding styles where everything must use forEach&#x2F;map&#x2F;reduce
评论 #36680996 未加载
b20000almost 2 years ago
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.
评论 #36681171 未加载
评论 #36681135 未加载