I’m always suspicious of people who say that a language is suboptimal and use as evidence some filthy one-liner. Maybe if you bothered to write some whitespace and didn’t write the language ignorant of its features (like generator expressions) you would end up with better code?
sum(
all(
abs(x) >= 1andabs(x) <= 3for x in line
) and (
all(x > 0for x in line) orall(x < 0for x in line)
)
for line in diffs
)
You no longer have to “jump back and forth” except one single time - you have to look to the end to see where line is coming from and then you can read the body of the main expression from start to finish.
People don’t, in fact, read code from top to bottom, left to right; they read it by first looking at its “skeleton” - functions, control flow, etc - until finding the bit they think is most important to read in detail. That implies that “jumping back and forth” is a natural and necessary part of reading (and hence writing) code, and so is nothing to fear.
There is still a slight advantage to not having to jump around, but consider the costs: in Javascript, map and filter are methods on Array and some other types. So how are you going to implement them for your custom iterable type? Do you have to do it yourself, or write lots of boilerplate? It’s easy in Python. It’s not bad in Rust either because of traits, but what this all means is that to get this, you need other, heavy, language features.
In practice, you often know what a comprehension is iterating over due to context. In those situations, having what the comprehension produces be the most prominent is actually a boon. In these scenarios in Rust/JS you are left skipping over the unimportant stuff to get to what you actually want to read.
People don’t, in fact, read code from top to bottom, left to right
100% this.
This false premise is also why a few (objectively wrong) people defend writing long essays: functions with hundreds of lines and files with thousands; saying “then you don’t have to go back and forth to read it”, when in fact, no one should be reading it like a novel in the first place.
Once you get used with list and dict comprehensions, they read just fine. Much like the functional approach is not really that readable for a newcomer either.
I agree with you that the one liner isn’t a good example, but I do prefer the “left to right” syntax shown in the article. My brain just really likes getting the information in this order: “Iterate over Collection, and for each object do Operation(object)”.
The cost of writing member functions for each class is a valid concern. I’m really interested in the concept of uniform function call syntax for this reason, though I haven’t played around with a language that has it to get a feeling of what its downsides might be.
I was also thinking about UFCS. I do like it for its flexibility, but I did try it in Nim one time and was left feeling unsure. Unfortunately I now can’t remember what exactly I didn’t like about it.
Not a single time did OOP talk about readability. That was not a point at all, so I don’t know why you are all about readability.
It was all about having a language that the IDE can help you write in because it knows what you are talking about from the beginning of the line.
The issue with the horrible one-liner (and with your nicely split-up version) is that the IDE has no idea what object you are talking about until the second-to-last non-whitespace character. The only thing it can autocomplete is “diffs”. Up until you typed the word, it has no idea whether sum(), all(), abs(), <, >, or for-in actually exist for the data type you are using.
If you did the same in Java, you’d start with diffs and from then on the IDE knows what you are talking about, can help you with suggesting functions/methods, can highlight typos and so on.
I think rust’s iterator chains are nice, and IDE auto-complete is part of that niceness. But comprehension expressions read very naturally to me, more so than iterator chains.
I mean, how many python programmers don’t even type hint their code, and so won’t get (accurate) auto-complete anyway? Auto-completion is nice but just not the be-all and end-all.
Fair, I missed one word. You missed the whole blog post.
It’s a big difference between writing code and writin APIs, tbh. If you write crap code that’s your problem. If you write crap APIs it’s the problem of anyone using your API.
The blog post is really about language design, because you definitely should not write a filter method for your custom iterable class in python; you should make it use the language’s interface’s for “being an iterable”. Language design involves APIs offered by the language, but isn’t really the purview of most people who write APIs.
If a suggestion on language design would gain something at the cost of readability, anyone should be very skeptical of that.
Those things together explain why I am evaluating the post mostly in terms of readability.
I’m always suspicious of people who say that a language is suboptimal and use as evidence some filthy one-liner. Maybe if you bothered to write some whitespace and didn’t write the language ignorant of its features (like generator expressions) you would end up with better code?
sum( all( abs(x) >= 1 and abs(x) <= 3 for x in line ) and ( all(x > 0 for x in line) or all(x < 0 for x in line) ) for line in diffs )
You no longer have to “jump back and forth” except one single time - you have to look to the end to see where
line
is coming from and then you can read the body of the main expression from start to finish.People don’t, in fact, read code from top to bottom, left to right; they read it by first looking at its “skeleton” - functions, control flow, etc - until finding the bit they think is most important to read in detail. That implies that “jumping back and forth” is a natural and necessary part of reading (and hence writing) code, and so is nothing to fear.
There is still a slight advantage to not having to jump around, but consider the costs: in Javascript,
map
andfilter
are methods onArray
and some other types. So how are you going to implement them for your custom iterable type? Do you have to do it yourself, or write lots of boilerplate? It’s easy in Python. It’s not bad in Rust either because of traits, but what this all means is that to get this, you need other, heavy, language features.In practice, you often know what a comprehension is iterating over due to context. In those situations, having what the comprehension produces be the most prominent is actually a boon. In these scenarios in Rust/JS you are left skipping over the unimportant stuff to get to what you actually want to read.
100% this.
This false premise is also why a few (objectively wrong) people defend writing long essays: functions with hundreds of lines and files with thousands; saying “then you don’t have to go back and forth to read it”, when in fact, no one should be reading it like a novel in the first place.
Once you get used with list and dict comprehensions, they read just fine. Much like the functional approach is not really that readable for a newcomer either.
The blog post wasn’t about reading, but about writing. And people usually do write top-to-bottom, left-to-right.
The whole point of the blog post was to write code that the IDE can help you with when writing. It didn’t go into readability even once.
the last section before the conclusion only mentions readability
What about all the other sections?
I agree with you that the one liner isn’t a good example, but I do prefer the “left to right” syntax shown in the article. My brain just really likes getting the information in this order: “Iterate over Collection, and for each object do Operation(object)”.
The cost of writing member functions for each class is a valid concern. I’m really interested in the concept of uniform function call syntax for this reason, though I haven’t played around with a language that has it to get a feeling of what its downsides might be.
I think Nim is the frontrunner here. Close to Python to write because it is so expressive, close to C speed because it is compiled properly.
I was also thinking about UFCS. I do like it for its flexibility, but I did try it in Nim one time and was left feeling unsure. Unfortunately I now can’t remember what exactly I didn’t like about it.
Did we read the same blog post?
Not a single time did OOP talk about readability. That was not a point at all, so I don’t know why you are all about readability.
It was all about having a language that the IDE can help you write in because it knows what you are talking about from the beginning of the line.
The issue with the horrible one-liner (and with your nicely split-up version) is that the IDE has no idea what object you are talking about until the second-to-last non-whitespace character. The only thing it can autocomplete is “diffs”. Up until you typed the word, it has no idea whether sum(), all(), abs(), <, >, or for-in actually exist for the data type you are using.
If you did the same in Java, you’d start with
diffs
and from then on the IDE knows what you are talking about, can help you with suggesting functions/methods, can highlight typos and so on.That was the whole point of the blog post.
I dunno, did we?
I think rust’s iterator chains are nice, and IDE auto-complete is part of that niceness. But comprehension expressions read very naturally to me, more so than iterator chains.
I mean, how many python programmers don’t even type hint their code, and so won’t get (accurate) auto-complete anyway? Auto-completion is nice but just not the be-all and end-all.
Fair, I missed one word. You missed the whole blog post.
It’s a big difference between writing code and writin APIs, tbh. If you write crap code that’s your problem. If you write crap APIs it’s the problem of anyone using your API.
The blog post is really about language design, because you definitely should not write a
filter
method for your custom iterable class in python; you should make it use the language’s interface’s for “being an iterable”. Language design involves APIs offered by the language, but isn’t really the purview of most people who write APIs.If a suggestion on language design would gain something at the cost of readability, anyone should be very skeptical of that.
Those things together explain why I am evaluating the post mostly in terms of readability.