Before we start, I do realize that this may potentially come off as a little harsh or negative, but that is not my intent, I am just curious as to ...
For further actions, you may consider blocking this person and/or reporting abuse
I don't want to sound bitter but...
I don't get the idea of writing convoluted examples to force your point of view...
How is this hard to read?
And why would you rather write code like this?
Single character variables?
s.length -1 -i? Creatingifstatements to returntrue/false?How s this more readable to you?
This is exactly the discussion I want us to have =)
as for the particular example you highlight (and I do take your point about "s", although "i" as a common name for an iterator.. well, its quite common)
In my view what you can see there is code that more explicitly mirror the actual functions "action", specifically it explicitly does what you'd do to see if a string is a palindrome or not, ie, you check the "mirror" character to equal the current character.
Using an if statement for an early out I feel also is quite handy, as soon as its apparent that its not a palindrome, we return, without having to process the entire string in those cases.
Choosing implementation variants is straightforward if you adhere to a predefined criteria set.
I write about how to evaluate implementations on criteria here:
TLDR: Choose the fastest implementation considering equal robustness.
dev.to/functional_js/squeezing-out...
In this case, the loop is many magnitudes faster. Use the loop.
Very clear comparison of the actual performance of the two.
I do wonder though why declaring the actual functions as "function Name(arg) { ..." has fallen somewhat out of favor, it just seems such a more natural way to do it given how the English language works.
Then again in most other languages you'd typically see:
returnType FunctionName(args) {
or something similar to that
That's a good question, here's my answer to it:
I don't understand why people still use the "function" keyword, unless you are accessing the "this" keyword, which should be never, except for legacy JavaScript.
Plus I would pick one consistent strategy and go with it, instead of intermingle, "sometimes arrow and sometimes function".
I also don't use single quotes. Everything double quotes.
Then there's none of this, "sometimes this, sometimes that" sprinkled all over the place.
I'm a minimalist, and only use a subset of the language.
Here is a list of what I don't use in JavaScript:
dev.to/functional_js/what-subset-o...
Thanks for that response.
I agree, consistency is key. Which is why on my projects there are always a set of coding guidelines that are enforced in terms of naming, spacing, comments, all that.
I do use the "this" keyword relatively frequently however myself, specifically from within member functions of an object, to access member data or other member functions.
It's about readability and understandability.
I never aim to write functional code. I aim to write what I consider to be the best code I can write.
First example
In the palindrome example, the function body is 2 lines long:
The function body of the loop version has 4 lines of code and a few more lines with braces. The volume of code to read is much higher, and you must read all of it before you understand what it's doing. Furthermore, you have to actually parse the logic in your head and see how it works, which I find much more difficult than the simpler logic in the functional example.
"reverse the string and check if it's equal to the original", compared to "loop through the string, for each index, grab the current character, check if the current character is equal to the character at the index from the opposite end of the string and return false if it isn't".
It just takes longer to read and understand for someone who is well-versed in both versions.
About performance: In many apps, it brings no benefit to optimise performance for simple things (premature optimisation). It's usually better to optimise for readability and only optimise for performance if needed.
Second example
I would have written the second example like this:
Using a library, or coding your own versions of these utilities, is fine, since you'll reuse those functions in many places throughout your codebase (DRY).
Again, if you understand what this is doing (which just takes a bit of practice), this is instant to read and understand. It says, "for every item in the list, add it to the total, with the total starting at 0".
The for-loop version is fine, but again, you have to read every word and parse it to understand it.
And so on.
Edit: Fixed the code.
Peer pressure? Almost everyday I read an article that has the following:
Their solution? Writing in the way you describe in this article. And this is the kind of content that influence new developers. Some may end up obsessed with immutability and "being declarative" to the point they don't consider performance at all.
The responses that you quoted are valid, but difficult to stand on their own. I much much prefer declarative code -- it does what it says. In the context of JavaScript, particularly in browser, the performance argument is debatable.
Any extremes without considering BOTH readability AND performance, will make someone unhappy. On the other extreme if you optimize for just performance, recursion should never be used -- everything should be written with imperative and iterative code. On this extreme, we have the competitive programmers with single name mutable variables and no spaces between operators -- compact, concise, and performant.
Cargo culting is bad if people don't change their perspectives given new information. I think it's fine that newcomers come in and stand on one side first without having to be paralyzed by having to think about both, but for an experienced dev, I'd imagine the expectation to be different and in mixed paradigm languages, like JavaScript, things aren't mutually exclusive (we can have both), but in a functional language -- ALWAYS use declarative code as the VM will optimize it anyway (e.g., tail-call optimization, and immutable data-types actually reference the same address in memory)
None of the examples shown are declarative.
I think people are somehow confusing point free code with declarative code, which is quite different.
You can put the points back in to make this obvious.
I think you're right -- I mean the functions themselves are not, but the devil's in the details... as I would consider the use of
reverseto be declarative within the body of the function.[via Wikipedia]
There is no logic expression that describes control flow in the use of the
reversemethod.That said, I don't believe point-free code and declarative code are mutually exclusive -- SQL comes to mind
With that definition, all procedure calls are declarative, which seems incorrect to me.
A declaration describes the state of the universe, a procedure call makes things happen.
What things it makes happen might be hidden from you, but the call itself is an explicit operation at a point in time, containing those hidden operations.
I see what you're getting at. I'm really just talking specifically about the method,
reverse, afforded by arrays in JavaScript. I'm not talking about the body of the function which is what I believe you to be interpreting here. This is why I agree with you that the examples used are not declarative.However, I'm wondering how you would describe the difference between the following, because to me, the first example, in my opinion, is imperative and procedural:
The second example says what we're doing rather than how we do it: "reduce the numbers to a sum total", whereas the first example's body has order dependence and is imperative that we declare and initialize
sbefore looping across the input numbers and then adding tos...Like, how would you describe the "sum" operation in the absence of the term, "declarative"?
[via Wikipedia]
I think you're conflating declarative programming here with a declaration.
I'm really just honing in on the paradigm itself and how operations are expressed -- functions that say what we're doing as opposed to how we do it in the spirit of how "declarative programming" is often understood and described
I'm not sure I understand, if this is a satire piece then I missed it completely.
All the examples provided, are far more readable than the author's own examples. Perhaps, it is because those operations are inspired by Functional Programming concepts, and has commonality across many different languages.
Putting performance aside (for now), these examples shows intent. It tells the 'what' rather than the 'how'. In one glance, I can tell what the first code is supposed to be doing, not so much with the for loop of the second one where I have to inspect and step-through the algorithm (in my head).
In my personal experience as well, for loop implementations need to be accompanied with comprehensive tests as it is easy to make a mistake in them.
Performance matters, but I do believe readability and correctness matters more.
I would have been fine if the focus of the article was simply on performance of conventional, imperative JS code. However, it should be acknowledged that readability and comprehension is way lower.
The article posts a question "why", and I think there has been quite a few potential answers to that question.
Not sure where you see many issues with loops though, care to elaborate with examples? =)
Missed the reply notification 😬
Not so much of a problem, but rather that for loops carry higher cognitive overload, whereas
maporfilteris composable and easier to comprehend (e.g. treating it as data pipeline).So there is definitely a tradeoff, readability vs performance.
I think it comes down to the idea that the less state and logic you have to manage the less bugs you'll end up with. Now that might not be true, but intuitively it makes sense. If there's less things I have to manage in my head, the easier things are to understand and the harder they are to mess up.
In your
capitalizeAllWordsexample the longer example has 5 variables to manage, while the functional version has 1 variables (the input string). A lot of the code in the imperative version doesn't pertain to what the function is doing (setting up the loop, setting up the intermediate variables, changing the variable state), while the functional version is almost completely just the operations you are interested in to complete the operation, and includes only one logical branch doing the input type check.I'd argue that speed of execution doesn't really matter at the small scale, for Javascript. For things like rendering a Virtual DOM, it will dwarf the execution time compared to running a small user created function.
Good points for sure. I'd argue the short version has two variables (sentence and word), but I take your meaning : ).
There is ofc more actual "variables" or pieces of data at least than that, just that they are never named, thus potentially they do not count.
The performance reflection is ofc also true, its more the general pattern of not caring that eventually gets us into trouble.
This post just reminds me how badly we need a better standard library in JavaScript. This is what I mostly miss from python.
As someone who has never used python, do you have any specifics to share on how something like that would look and work? : )
A lot of this stuff regarding string and list(array) manipulation in python can be just a one liner.
without deep knowledge of how python works that is not intuitive for me sadly. Might be time to look at some python stuff to get better insight =)
That's fine. The only issue is the unfamiliarity with
[::-1]or[:n]or[start:end]syntax. This works likeArray.slice()for reading lists and strings. It just says you want to capture the list/string between astartandendindex. The negative one means read the string backwards.Looked it up just now as well, there are certainly some powerful capabilities there, including the stride thing, real nice.
There is a lot of room for ergonomic improvements in JS. But it will forever be hampered by the "compiler" being on the remote system.
Anders, I think you need to see this:
dev.to/eelstork/introducing-howl-a...
: ))
Concision is valuable on its own. When concision works against clarity, there is a problem. How do you balance these two?
Not that simple. I'm not a huge fan of lambdas, but then again, as a programmer I predate (the widespread adoption of) lambdas. So there's an issue here of:
You are showing "not much longer" code which ends up 3x 4x longer than the alternative. Programmers spend most time reading code and navigating code bases they know.
3x shorter, I think that's valuable when scanning through thousands of lines of code.
There isn't a standard of clarity per se (well; linters, good practices and so forth, but that is definitely not the end of it). What matters is what works for you, and your team.
Now, talking performance, the answer is really simple. BEFORE benchmarking your code there should be a perceived need for the code to run faster. Optimise for readability, concision and productivity. These concerns duly override worrying about how fast your code is running.
∘ ㅇ IsModifier(ㄹ x) {
⤴ (howlTemplate ☰ null) ⤬
⤵ ∀ (∙ k ∈ howlTemplate) ⤴ (k ☰ x) ㆑ ⤬
}
Ohh my =)
Does this count as an esoteric language?
I generally agree, though I think the examples you've given weren't too bad in terms of readability...
... and I'd like to point out that doing the "old fashioned" version of
captilizeAllWordsyou've introduced a bug. Because you're not splitting on words, a double-space in the string will mess it up. That's a mistake that's harder to make using the terser format.I do think that a lot of (especially new) Javascript coders are trying to be as terse as possible because it maybe makes them feel more advanced, or that they're doing things the "best practice" or "modern" way, but the point this post is making stands:
The style of code that you write affects how other people read and maintain it far more than it affects the application itself.
Performance is important, but premature optimisation is the cube root of all evil... and less code doesn't necessarily give more performance. Loop unrolling, for example, is a recognisable way to improve performance but it means you write more code in a messier way that future you is gonna hate.
Quite possibly you are right there, its just "the pattern" that stands out, not necessarily that these where the worst offenders by any stretch of the imagination =)
Actually, if you do run that version of captilizeAllWords, ex: captilizeAllWords("will this fuck up?") => "Will This Fuck Up?" so it does work, unless ofc you also want it to remove any double spaces...
Your observation seems accurate to me, it seems terseness and/or cleverness is weighing too highly on the scales, especially for newer coders.
And I do as well agree that you should (mostly) not sacrifice readability for performance, but you also should not totally disregard performance, lest we all end up in the death by a thousand cuts scenario.
You're right, I didn't try running it and it's obvious it'll work now I look again. But that's part of the problem, isn't it? It's not always obvious how the flow goes no matter what style you use!
So true, that is our never ending challenge =)
You're basically comparing functional style vs imperative style. Functional style is higher level (as in C is higher level than assembly) and we often compose generic functions that can be applied in multiple situations such as
reduce(used to loop through and accumulate values over collections).It may seem less readable to the untrained eye but when you're used to it, you appreciate not having to think about the low level details, which often imply mutation, state management and so on, which with enough time will grow spaghetti and become difficult to maintain.
That is because Javascript, even though it has the concept of iterables and generators for ages, still does not use them enough.
Take Python, for instance:
reversed(x)does not allocate memory ifxis randomly indexable. Same formapandfilter, which don't execute until you require them. That means:const [x] = arr(x => x ** 2)Should only execute the callback ONCE, as we are extracting just the first element. In plain JS, however, it executes for everything and returns a new array.
Why do we right code like this?
This allocates 2 1-character strings, and performs a string comparison (not a character comparison, there is no character datatype in Javascript).
If efficiency is our criteria, then we would write
That is great, how does that perform compared to what I wrote?
That is, of course, the right question.
In Node 12, it seems to make no difference at all. Maybe they optimize to the same thing.
Results may vary in other runtimes.
I think we're at this weird time in programming where functional concepts are becoming wider spread, but still not ubiquitous enough that you can necessarily assume people will be familiar with them. It's possible that over the next 10 years FP will become the dominant programming paradigm and OOP will become legacy or restricted to certain use cases.
In that world I would expect the FP style will become familiar to a majority of programmers and so won't be strange to see it in examples.
I also I think it represents a gap between enthusiasts who produce blog posts and tend to be up on the latest code style trends and the majority of programmers who mostly work in an OOP/imperative style.
I have little time to participate in this discussion but I have some 1 opinion, 1 free resource, and 1 paid resource:
1) For me, understanding this problem is the difference between seniors and juniors developers... Junior developers tend to think that programming is a bidimensional activity driven by Skills and Knowledge. After some experience as a programmer, you start to realize that the most important thing about creating software is the scalability and maintainability. It is more expensive to maintain code than creating it. So, anything that you can do to make maintainability easier(a.k.a. cheaper) is welcome that includes abstracting implementation details.
2)This whole video answers your question: frontendmasters.com/teachers/kyle-...
3)I recommend you take the Functional Javascript of Kyle Simpson the author of the book 'You Don't Know JavaScript', unfortunately, it is not a free resource but it is worth much more than what it cost. Highly, recommend the frontend master courses I have paid 3 months now and I feel with a higher level on my JavaScript.
frontendmasters.com/courses/functi...
Well, bad poetry isn't to everyone's taste, so it's understandable.
Mostly I wanted to convey that I think that I understood their position, and that it was a fine personal choice, but might lead to a situation that they would eventually come to regret.
Without framing it as an argumentative position, which is the tricky bit.
Makes me think of an interesting split of opinion or approach or maybe programming temperament.
Some folk focus on the logic of what they are trying to achieve, they like functions and hate pointers. Others like to know more about what the computer is actually doing, they love pointers and benchmarking.
I'm being simplistic but it made me wonder...
I have never ever actually had a problem with JavaScript running too slowly. Only ever issues with performance of complex graphical operations stemming from low-level. The readability that this functional, immutable style of JS provides has, however, been incredibly beneficial to me in terms of being able to wrap my head around large codebases.
If concerned about performance over functional clarity should we be even using JavaScript? Rather than web assembly?
That may certainly be an option if you do bot need to support older browsers. My focus here though was specifically JavaScript patterns. I think we can all agree that JS isn't exactly the perfect language.
I draw the line at: if I have to Google how to make this code a one-liner then maybe it's not right to do.
Hehe, that sounds like the ultimate sanity check =)