9 ms·
The first example complained about the refactor appealing to functional thinkers (implying that it would be difficult to grok by the existing devs), but then th
by michaelteter 2y ago
The first example complained about the refactor appealing to functional thinkers (implying that it would be difficult to grok by the existing devs), but then the “improved” version is virtually the same save for the (unnecessary?) use of Ramda in the first.
And while many devs are resistant to try functional ways, this first example reads so much better than the original code that I find it impossible to believe that some prefer the imperative loop/conditional nesting approach.
- happytoexplain 2y agoAre you saying you find it hard to believe that some prefer the first "Before" example over the first "Bad refactor" example?
- p2501 2y agoAesthetic aside, I am under the impression that people start programming, by and large, with imperative for/if style => so the imperative style is readable by more people. Even for more experienced programmers, reading imperative probably cost less energy, since it is more internalised? Futhermore, in JS, the functionnal style is less performant (nearly twice on my machine, i assume because it do less useless memory allocations) So, same functionnality, readable by more people, more performant? The imperative example seems like the better code.
- joshka 2y agoI, a Datapoint of 1, find the functional style to generally express the ideas of what's happening to be significantly easier to grok. Particularly if intermediate variables and conversion constructors are introduced rather than relying on full chains. E.g.: function processUsers(users: User[]): FormattedUser[] { let adults = users.filter(user => user.age >= 18); return adults.map(user => FormattedUser.new_adult(user)); } On the performance tip, what scale are we talking? Is it relevant to the target system? Obviously the example is synthetic, so we can't know that, but does it seem like this would have a runtime performance that is meaningful in some sort of reasonable use case?
- zahlman 2y ago> I am under the impression that people start programming, by and large, with imperative for/if style => so the imperative style is readable by more people. IMO, this is a simple consequence of technology moving faster than society. There are still instructors out there who learned to program in an environment where the go-to options for imperative programming were C and FORTRAN; the go-to options for other paradigms (if you'd even heard of other paradigms) were things like Lisp, Haskell and Smalltalk; and CPU speeds were measured in MHz on machines that you had to share with other people. Of course you're going to get more experience with imperative programming; and familiarity breeds comprehension. But really, I believe strongly that the functional style - properly factored - is far more intuitive. The mechanics of initializing some output collection to a default state (and, perhaps, the realization that zero isn't a special case), keeping track of a position in an input collection, and repeatedly appending to an output, are just not that interesting. Sure, coming up with those steps could be a useful problem-solving exercise for brand-new programmers. But there are countless other options - and IMX, problem-solving is fiendishly hard to teach anyway. What ends up happening all the time is that you think you've taught a skill, but really the student has memorized a pattern and will slavishly attempt to apply it as much as possible going forward. > Futhermore, in JS, the functionnal style is less performant (nearly twice on my machine, i assume because it do less useless memory allocations) Sure. Meanwhile in Python: $ python -m timeit "x = []" "for i in 'example sequence':" " x.append(i)" 500000 loops, best of 5: 796 nsec per loop $ python -m timeit "x = [i for i in 'example sequence']" 500000 loops, best of 5: 529 nsec per loop ... But, of course: $ python -m timeit "x = list('example sequence')" 2000000 loops, best of 5: 198 nsec per loop Horses for courses.
- vasco 2y agoPeople think imperatively though. If I think of visiting my friend, grabbing gas on the way back, the way I'll visualize the steps is not functional.
- The_Colonel 2y agoThat's quite debatable. In this case, you first declare the end-goal - visit a friend and have full gas tank, with the actual steps to achieve them being much less important and often left to be defined at a later point (e.g. which particular gas station, which particular pump etc.). This corresponds more to functional thinking. An imperative thinking would correspond more to "I will sit in the car, start the engine, ride on highway, stop at address X, converse with Y, leave 2 hours later, stop at gas station X" - in this case the imperative steps are the dominant pattern while the actual intent (visit a friend) is only implicit.
- The_Colonel 2y ago> Even for more experienced programmers, reading imperative probably cost less energy, since it is more internalised? I disagree. For-cycles are usually more difficult to reason about, because they're more general and powerful. If I see "for (...", I only know that the subsequent code will iterate, but the actual meaning has to be inferred from the content. Meanwhile, a .map() or .filter() already give me hints - the lambda will transform the values (map), will filter values (filter), these hints make it easier to understand the logic because you already understand what the lambda is meant to do. Other benefits stem from idiomatic usage of these constructs. It's normal to mix different things into one for-cycle - e.g. filtering, transformation, adding to the resulting collection are all in the same block of code. In the functional approach, different "stages" of the processing are isolated into smaller chunks which are easier to reason about. Another thing is that immutable data structures are quite natural with functional programming and they are a major simplification when thinking about the program state. A given variable has only one immutable state (in the current execution) as opposed to being changed 1000 times over the course of the for-loop.
- math_dandy 2y agoNo need to fear mutable local state. Shared state is where immutable data structures really shine.
- persnickety 2y ago> If I see "for (...", I only know that the subsequent code will iterate And then someone slaps do {} while(0) in a macro.
- diatone 2y ago> I find it impossible to believe that some prefer the imperative loop/conditional nesting approach. Yeah, there’s your problem. This is in fact possible!
- skybrian 2y ago(Raises hand.) I prefer the for loop. Pushing items to an array is idiomatic Javascript for creating an array. An if statement is an idiomatic way to do it conditionally. It's also easier to debug. The map and filter methods are nice too, but they're for one-liners.
- nine_k 2y agoWriting assembly was the idiomatic way of programming before Fortran and human-readable languages came. Writing with goto was the idiomatic way before Algol and structural programming came. Having only a handful of scalar types was the idiomatic way until structural data types came (and later objects). Writing programs as fragments of text that get glued together somehow at build time was the idiomatic way until module systems came. (C and partly C++ continue to live in 1970s though.) Callback hell was the idiomatic way to do async until Futures / Promises and appropriate language support came. Sometimes it's time to move on. Writing idiomatic ES5 may feel fun for some, but it may not be the best way to reach high productivity and correctness of the result.
- skybrian 2y agoMaking analogies like this doesn't prove anything, they're just suggestive. All I'm getting out of this is that you think for loops are old-fashioned.
- Jean-Papoulos 2y agoThat's because they are. Functional code is more readable. And if you look back, basically all advances in programming languages have been about "making stuff more readable". Thus, for loops (for this usage) are "old".
- skybrian 2y agoYou say it’s more readable and I disagree with that! Is this just fashion? Is there a way to settle it other than “I like it better?”
- jamil7 2y agoI've haven't written JS in a long time – are engines like V8 smart enough to roll the filter and map into a single loop? Otherwise wouldn't a reduce be more efficient there?
- nevon 2y agoIt's not a matter of being smart enough. Since JavaScript is interpreted, the optimization happens at runtime. If the code is executed once and the number of items in the array is small, then it will take more time for the compiler to optimize the code than to naively execute it. Most code falls into this category. As for whether or not it's possible at all to combine a map and filter into a single loop I guess depends on whether the first operation can have side effects that affect the second operation or the collection that is being iterated over. I don't know the answer, but I would be surprised if there wasn't some hard to detect corner case that prohibits this kind of optimization.