I'm curious on how much of this is basically overcome by the new tools? As much as vibe coding annoys me, I can't argue against how good autocomplete from these LLMs can be.
> This is much more pleasent. Since the program is always in a somehwat valid state as you type it, your editor is able to guide you towards the Pit of Success.
Although the subtitle was “programs should be valid as they are typed”, it’s weakened to “somewhat valid” at this point. And yes, it is valid enough that tooling can help, a lot of the time (but not all) at full capability. But there’s also interesting discussion to be had about environments where programs are valid as they are typed. Syntactically, especially, which requires (necessary but not sufficient) either eschewing delimition, or only inserting opening and closing delimiters together.
That would be nice if devs always wrote code sequentially, i.e. left to right, one character at a time, one line at a time. But the reality is that we often jump around, filling in some things while leaving other things unfinished until we get back to them. Sometimes I'll write code that operates on a variable, then a minute later go back and declare that variable (perhaps assigning it a test value).
> If you aren’t familiar with Rust syntax, |argument| result is an anonymous function equivalent to function myfunction(argument) { return result; }.
> Here, your program is constructed left to right. The first time you type line is the declaration of the variable. As soon as you type line., your editor is able to suggest available methods.
Yeah, having LSP autocomplete here does feel nice.
But it also makes the code harder to scan than Python. Quick readability at a glance seems like the bigger win than just better autocomplete.
I miss the F# pipe operator (https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...) in other languages. It's so natural to think of function transform pipelines. In other languages you have to keep going to the left and prepend function names, and to the right to add additional args, parens etc ...
len(list(filter(lambda line: 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])), diffs)))
This really isn’t fair on Python. Python isn’t very much not designed for this style of functional programming. Plus you haven’t broken lines where you could. Rewrite it as a list comprehension and add line breaks, and turn the inner list comprehensions into generator expressions (`all([…])` → `all(…)`), and change `abs(x) >= 1 and abs(x) <= 3` to `1 <= abs(x) <= 3` (thanks, Jtsummers), and it’s much better, though it still has the jumping around noted, and I do prefer the functional programming approach. I’m just saying the presentation isn’t fair on Python.
len([line for line in diffs
if all(1 <= abs(x) <= 3 for x in line)
and (all(x > 0 for x in line) or all(x < 0 for x in line))])
(Aside: change the first line to `sum(1 for line in diffs` and drop the final `]`, and it will probably perform better.)
I also want to note, in the JS… Math.abs(x) instead of x.abs() (as seen in Rust).
And, because nerd sniping, two Rust implementations, one a direct port of the JS:
Don’t know why python gets so much love. It’s a painful language as soon as more than one person is involved. What the author describes is just the tip of the iceberg
I love python, as long as were are talking about small teams, short, and short lived programs. The lack of static types makes things quick to implement even if it isn't too sound, and the strong typing keeps you from fucking up too badly. That is why I think it gets so much love in data science, it is great for noodling around while you are trying to figure something out.
On the other hand if you are going to be building something that is going to be long lived, with multiple different teams supporting it over time, and\or larger programs where it all doesn't fit in (human) memory, well then python is going to bite you in the ass.
There isn't a one size fits all programming language, you need at least two. A "soft" language that stays out of your way and lets you figure things out, and a "hard" language that forces the details to be right for long term stability and support.
It's the exceptional codebase that's nice to work with when it gets large and has many contributors. Most won't succeed no matter the language. Language is a factor, but I believe a more important factor is caring a lot.
I'm working on a python codebase for 15 years in a row that's nearing 1 million lines of code. Each year with it is better than the last, to the extent that it's painful to write code in a fresh project without all the libraries and dev tools.
Your experience with Python is valid and I've heard it echoed enough times, and I'd believe it in any language, but my experience encourages me to recommend it. The advice I'd give is to care a lot, review code, and keep investing in improvements and dev tools. Git pre commit hooks (just on changed modules) with ruff, pylint, pyright, isort, unit test execution help a lot for keeping quality up and saving time in code review.
While methods partially solve this problem, they cannot be used if you are not the author of the type. Languages with uniform function call syntax like Nim or D do this better.
Disagree. The first example the author seem to want something more like imperative programming, so the "loop" construct would come first. But then the assignment should come last. With the python syntax you get the thing you're assigning first - near the equals sign - and then where it is selected from and with any filtering criteria. It makes perfect sense. If you disagree that's fine, the whole post is an opinion piece.
Incidentally, Darklang had this built into the language/editor combo from the start. You can see it in the examples on our youtube, maybe this one: https://www.youtube.com/watch?v=NQpBG9WkGus
To make a long story short, we added features for "incomplete" programs in the language and tools, so that your program was always valid and could not be invalid. It was a reasonable concept, and I think could have been a game changer if AI didn't first change the game.
SQL shows it's age by having exactly the same problem.
Queries should start by the `FROM` clause, that way which entities are involved can be quickly resolved and a smart editor can aid you in writing a sensible query faster.
The order should be FROM -> SELECT -> WHERE, since SELECT commonly gives names to columns, which WHERE will reference.
You could even avoid crap like `SELECT * FROM table`, and just write `FROM table` and have the select clause implied.
Never mind me, I'm just an old man with a grudge, I'll go back to my cave...
> The order should be FROM -> SELECT -> WHERE, since SELECT commonly gives names to columns, which WHERE will reference.
Internally, most SQL engines actually process the clauses in the order FROM -> WHERE -> SELECT. This is why column aliases (defined in SELECT) work in the GROUP BY, HAVING and ORDER BY clauses, but not in the WHERE clause.
I've always liked the `select...from` order because it helps me understand the goal before reading the logic. In other words, I want to end up with this, and here's how I want to go about getting it.
Some IDEs provide code templates, where you type some abbreviation that expands into a corresponding code construct with placeholders, followed by having you fill out the placeholders (jumping from one to the next with Tab). The important part here is that the placeholders’ tab order doesn’t need to be from left to right, so in TFA’s example you could have an order like
{3} for {2} in {1}
which would give you code completion for {3} based on the {1} and {2} that would be filled in first.
There is generally a trade-off between syntax that is nice to read vs. nice to type, and I’m a fan of having nice-to-read syntax out of the box (i.e. not requiring tool support) at the cost of having to use tooling to also make it nice to type.
This is not meant as an argument for the above for-in syntax, but as an argument that left-to-right typing isn’t a strict necessity.
So many engineers define their own narrow ergonomic values and then turn to the interwebs attempting to hammer their myopic belief into others as evangelical truth -- this reads as if the author believes there is a singular left-to-right process that a developer ought to adhere to. The author is oblivious to the non-linear compositional practices of other coders. It would be much more constructive to spend time creating specific tooling to aid your own process and then ask others if the values are also relevant to them. Not every developer embraces LSP, for example, as some are thwarted by its opinionated implementation. Not everyone is willing to give up local structure for auto-complete convenience.
On the other hand, Python does have "from some_library import child_module" which is always nice. In JS we get "import { asYetUnknownModule } from SomeLibrary" which is considerably less helpful.
That's fine for doing algebra in pure functions, but what about destructive commands or interactive scenarios?
For example, using "rm" on the command line, or an SQL "delete". I would very much like those short programs to be invalid, until someone provides more detail about what should be destroyed in a way that is accident-resistant.
If I had my 'druthers, the left-to-right prefix of "delete from table" would be invalid, and it would require "where true" as a safety mechanism.
This is the main reason I really like concatenative syntax for languages — this property is _enforced_ for programs (minus some delimited special cases, usually). It also neatly generalizes the special `self` argument so you can dispatch on the types of all the arguments.
> Suppose you have a FILE file and you want to get it’s contents. Ideally, you’d be able to type file. and see a list of every function that is primarily concerned with files. From there you could pick read and get on with your day.
> Instead, you must know that functions releated to FILE tend to start with f, and when you type f the best your editor can do is show you all functions ever written that start with an f
Why do you think that this is a problem of C? no one is stopping your tools from searching `fclose` by first parameter type when you wrote `file.`. Moreover, I know that CLion already do this.
84 comments
[ 4.6 ms ] story [ 85.5 ms ] threadAlthough the subtitle was “programs should be valid as they are typed”, it’s weakened to “somewhat valid” at this point. And yes, it is valid enough that tooling can help, a lot of the time (but not all) at full capability. But there’s also interesting discussion to be had about environments where programs are valid as they are typed. Syntactically, especially, which requires (necessary but not sufficient) either eschewing delimition, or only inserting opening and closing delimiters together.
That would be nice if devs always wrote code sequentially, i.e. left to right, one character at a time, one line at a time. But the reality is that we often jump around, filling in some things while leaving other things unfinished until we get back to them. Sometimes I'll write code that operates on a variable, then a minute later go back and declare that variable (perhaps assigning it a test value).
> Here, your program is constructed left to right. The first time you type line is the declaration of the variable. As soon as you type line., your editor is able to suggest available methods.
Yeah, having LSP autocomplete here does feel nice.
But it also makes the code harder to scan than Python. Quick readability at a glance seems like the bigger win than just better autocomplete.
I also want to note, in the JS… Math.abs(x) instead of x.abs() (as seen in Rust).
And, because nerd sniping, two Rust implementations, one a direct port of the JS:
(`x.abs() >= 1 && x.abs() <= 3` would be better as `(1..=3).contains(x.abs())` or `matches!(x.abs(), 1..=3)`.)And one optimised to only do a single pass:
https://en.wikipedia.org/wiki/Non-English-based_programming_...
I've seen some SQL-derived things that let you switch it. They should all let you switch it.
On the other hand if you are going to be building something that is going to be long lived, with multiple different teams supporting it over time, and\or larger programs where it all doesn't fit in (human) memory, well then python is going to bite you in the ass.
There isn't a one size fits all programming language, you need at least two. A "soft" language that stays out of your way and lets you figure things out, and a "hard" language that forces the details to be right for long term stability and support.
I'm working on a python codebase for 15 years in a row that's nearing 1 million lines of code. Each year with it is better than the last, to the extent that it's painful to write code in a fresh project without all the libraries and dev tools.
Your experience with Python is valid and I've heard it echoed enough times, and I'd believe it in any language, but my experience encourages me to recommend it. The advice I'd give is to care a lot, review code, and keep investing in improvements and dev tools. Git pre commit hooks (just on changed modules) with ruff, pylint, pyright, isort, unit test execution help a lot for keeping quality up and saving time in code review.
To make a long story short, we added features for "incomplete" programs in the language and tools, so that your program was always valid and could not be invalid. It was a reasonable concept, and I think could have been a game changer if AI didn't first change the game.
Queries should start by the `FROM` clause, that way which entities are involved can be quickly resolved and a smart editor can aid you in writing a sensible query faster.
The order should be FROM -> SELECT -> WHERE, since SELECT commonly gives names to columns, which WHERE will reference.
You could even avoid crap like `SELECT * FROM table`, and just write `FROM table` and have the select clause implied.
Never mind me, I'm just an old man with a grudge, I'll go back to my cave...
There is generally a trade-off between syntax that is nice to read vs. nice to type, and I’m a fan of having nice-to-read syntax out of the box (i.e. not requiring tool support) at the cost of having to use tooling to also make it nice to type.
This is not meant as an argument for the above for-in syntax, but as an argument that left-to-right typing isn’t a strict necessity.
For example, using "rm" on the command line, or an SQL "delete". I would very much like those short programs to be invalid, until someone provides more detail about what should be destroyed in a way that is accident-resistant.
If I had my 'druthers, the left-to-right prefix of "delete from table" would be invalid, and it would require "where true" as a safety mechanism.
> Instead, you must know that functions releated to FILE tend to start with f, and when you type f the best your editor can do is show you all functions ever written that start with an f
Why do you think that this is a problem of C? no one is stopping your tools from searching `fclose` by first parameter type when you wrote `file.`. Moreover, I know that CLion already do this.
https://github.com/tc39/proposal-pipeline-operator
It would make it possible to have far more code written in the way you’d want to write it