These articles and comments on them always seem to conflate programming with programming at one's job. And it makes sense, for most people that's one and the same. But if we're talking about programming and art, I think that is a major distinction.
To provide an example, I will use an existing comment here from WCSTombs: "programming is primarily problem solving, which isn't fundamentally artistic because its main goal is correctness and fitness for purpose".
I don't consider the programming I do at work to be art or expect to ever approach art. It fits their definition. But I could see some of the programming I do for pleasure to fall within that category. Correctness is nice I guess, but fitness for purpose was never a consideration in my hobby programming, quite the opposite, actually.
I was programming for a long time before I ever starting programming for money, which is why I feel strongly about this distinction, but it may be that in the current day and age, I am just being a boomer about the whole thing.
This and "Intellectual Fly Is Open" being at the top of the Hacker News front page right now and both having their titles auto-editorialized by HN really makes me wonder what benefit this mechanism is supposed to bring readers other than needless confusion.
It's kind of like the Scunthorpe Problem[1]. Really, there's no better way to solve [whatever it is HN is trying to solve here] than text substitution??
While I have ranted already about the automated word removal being silly, too few posters are aware that you can edit the title after submission and auto-editorializing to restore anything that's been unnecessarily cut off.
Perhaps too many are aware, because the more people fix the auto-editorializing manually, the less inclined Dang will be to eventually see a reason to remove it after all.
It's an unaligned regular expression gone rogue. We became too dependent on our labor-saving string functions—not afraid enough of their corner cases, and lower cases. They were too useful. We began to normalize deviance, when we should have been normalizing Chomsky forms. We turned a blind eye to the unbounded growing evidence of something alarming.
We all know what this means, but few of us have the courage to say it - we must immediately halt all research on regular expression. None of us know how it works and it has terrorized us for too long.
Can an LLM be trained to understand language without remembering anything else from its training data? I thought the intrinsic knowledge and the ability to understand language were tied together.
It would be closer to using an LLM as a RAG for memory, as the reasoning LLM in injected with the return of the 'memory llm' (maybe with a defined number of 'slots' for easy clean up).
Reminds me of politicians and public figures saying they got "hacked" after doing/posting something stupid and getting backlash for it. I guess "AI did it" is the new iteration on that.
And kids. I recently happened to see a pic on a kid's phone of him holding 5 vapes and a tin of snus, when challenged on it he said it was an AI-generated fake.
My favorite was the congresscritter who held a press conference complaining that Google was returning obscene links to ordinary queries.
He didn't understand that Google was remembering his own past searches, and self-owned on camera. Turns out his wife and kids were more performative than interest-based.
The issue is fake news is real and has somehow been co-opted by the perpetrators to attack any claim/media group that is critical of them or their actions. If you fact check these people they just scream “fake news” until their followers chant it with them.
This comment has gone up and down like nothing I’ve ever commented before. Legitimately fascinating to watch. It’s gone from -1 back to 1 several times today.
This post doesn't touch on something that makes parsers complicated no matter how simple the grammar: good error messages. Parsing a well formed input is the easy part, but not just spitting out a byte index but actually telling the user why their input is not good and what they could do to make it conform is super hard.
The Rust compiler is a common example of a compiler that does a good job here, and I think it is one of only a few.
Built-in line and column tracking. Any movement across a newline updates the line number, including a backwards seek. getLine and getColumn are always available and both are one-based, which makes decent error messages nearly free.
That doesn't sound like much, but having hand-written plenty of recursive descent parsers, it's most of what you need for good error messages. Just being able to pinpoint where the error occurred is usually 80% of the battle; but keeping track of lines and columns in a hand-written parser is a pain.
Sure, for something like Rust, you need vastly more than that, but parsing is a tiny fraction of what the Rust compiler is doing -- type-checking and borrow checking is much more complicated and much more important.
A tiny library like this is a great fit for something like an INI file parser.
>> Built-in line and column tracking. Any movement across a newline updates the line number, including a backwards seek. getLine and getColumn are always available and both are one-based, which makes decent error messages nearly free.
> That doesn't sound like much, but having hand-written plenty of recursive descent parsers, it's most of what you need for good error messages.
In my experience having access to the appropriate place where the parser failed is necessary but wholly insufficient for good diagnostics.
I don't really agree. Many top-down parsers find an error at an unexpected token. That token is often not the error. Quite often something is missing at that point, or there has been a mistake some way back. Translating e.g. "unexpected semicolon" into "keyword 'if' should be the identifier 'f'" is not easy.
I think it becomes easier if you have some oracle, like a compiler, available to check whether the end result (after introducing suggestions) is viable.
I say it like this because to me the only valid way to come to the latter class of error messages (containing constructive suggestions) is by first coming up with possible edits and then checking whether they make the whole parse and compile.
Doaitse Swierstra’s parser combinators have included this for a while. I seem to recall them also having optional support for self-healing such as adding missing commas, parentheses, etc. I’m sure other parser combinators have this as well by now.
I will provide some context from having done a lot of that work.
The Rust grammar is actually quite regular, that's why we have things like the turbofish for type parameters (`binding.method::<Type>()`): it makes the grammar unambiguous (a naïve parser would with a complicated grammar that accepts chained comparisons would have to deal with differentiating between `binding.method < value > ()` and `binding.method<Type>()`). But that doesn't mean the rustc parser doesn't do the work of supporting some the more complex grammar in order to provide better diagnostics. I like to say that rustc actually knows about meta-Rust, a daughter language that goes crazier in its features. I also joke that rustc isn't done until you can paste code from another language and following the suggestions you end up with valid Rust code without loss of the user's intent.
Part of the problem is that the places where incorrect code can fail is in more places than the parser. The chained comparisons example is one that is easy for Rust (as it doesn't support them), so the parser itself can produce a "missing turbofish" suggestion with high certainty, but for truly ambiguous expressions, the errors will happen later, during name resolution ("expected a value and found a type") or when checking the number of arguments. A production compiler needs to account for not only the original error, but also silence every knock-down error too. The simplest strategies are to just stop if at the end of a given stage there are errors (which leads to the "wave of errors" experience of fixing the "last" error leading to a ton of new ones) or fully replacing entire blocks of code that had a parse error with an AST node that acts as a tombstone marking that that later stages need to ignore it. The first option leads to a bad experience, and the latter is insufficient. A recent example of looking at this is https://github.com/rust-lang/rust/pull/159689, where `Arc::new(RwLock::new(HashMap<i32, i64>::default()));` currently produces
error[E0423]: expected value, found struct `HashMap`
--> $DIR/suggest-turbofish-parsed-as-comparisons.rs:11:34
|
LL | let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
| ^^^^^^^
|
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
::: $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
|
= note: `HashMap` defined here
error[E0423]: expected value, found builtin type `i32`
--> $DIR/suggest-turbofish-parsed-as-comparisons.rs:11:42
|
LL | let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
| ^^^ not a value
error[E0423]: expected value, found builtin type `i64`
--> $DIR/suggest-turbofish-parsed-as-comparisons.rs:11:47
|
LL | let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
| ^^^ not a value
error[E0425]: cannot find external crate `default` in the crate root
--> $DIR/suggest-turbofish-parsed-as-comparisons.rs:11:53
|
LL | let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
| ^^^^^^^ not found in the crate root
error[E0061]: this function takes 1 argument but 2 arguments were supplied
--> $DIR/suggest-turbofish-parsed-as-comparisons.rs:11:22
|
LL | let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
| ^^^^^^^^^^^ --------------- unexpected argument #2 of type `bool`
|
note: associated function defined here
--> $SRC_DIR/std/src/sync/poison/rwlock.rs:LL:COL
help: remove the extra argument
|
LL - let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
LL + let _ = Arc::new(RwLock::new(HashMap<i32));
|
This is because the expression is syntactically correct as
RwLock::new( HashMap < i32, i64 > ::default() );
^^^^^^^^^^^^ ------- - ---^ --- - ----------- ^
| | | | | | | |
| | | | | | | a function call to `default` in the crate root
| | | | | | a more than binop
| | | | | a value to be compared
| | | | the separator of the second argument to `RwLock::new()`
| | | a value to be compared
| | a less than binop
| a value to be compared
an associated function call
but after that PR it would only be the following, even though the parser hasn't changed:
error: can't compare two types
--> $DIR/suggest-turbofish-parsed-as-comparisons.rs:24:41
|
LL | let _ = Arc::new(RwLock::new(HashMap<i32, i64>::default()));
| ^ ^ these are parsed as "less than" and "greater than"
|
help: you likely intended to write type `HashMap` with type parameters, but type parameters in expression contexts require the use of the "turbofish" `::<>`
|
LL | let _ = Arc::new(RwLock::new(HashMap::<i32, i64>::default()));
| ++
I think that there's a lot of work needed in the parser itself to produce good diagnostics. There are other strategies, like performing multiple parses at a given point when you've reached a known bad state (you've seen a flag-post that shouldn't be there, but that is a signal for a handful of other known cases), or fully consuming the rest of a block when an unrecoverable parse occurred (we're half-way through parsing function arguments, but failed? consume the rest of the statement or of the parent block, accounting for sub-scopes). The latter can cause the rest of the file to be consumed, but that's an edge-case that in practice is much better than a deluge of irrelevant errors.
Another added complexity is how some easy-to-hit errors occur during lexing, which means the compiler has barely any information about the user's code. Mismatched braces/parens is one of those. rustc tries to provide context by keeping a queue of seen open delimiters to point at, and explicitly checking for their indentation level as a heuristic to detect where the user's intent diverged from the code, but that's overly reliant on the code being sanely formatted (thanks to rustfmt-on-save, that's a good bet for many users). For an example of the things rustc can do even in the lexer, you can look at https://github.com/rust-lang/rust/pull/160592.
I have been using macOS for I think fewer than ten years and I think the attention of detail may always have been just about the hardware... macOs and iOS have presented me with some of the clunkiest edge cases and mishandled situations and a lot of them are damn near undebuggable. I think Windows is very tasteless OS making macOS seem good in comparison but in isolation there is a lot that could a should be improved
To provide an example, I will use an existing comment here from WCSTombs: "programming is primarily problem solving, which isn't fundamentally artistic because its main goal is correctness and fitness for purpose".
I don't consider the programming I do at work to be art or expect to ever approach art. It fits their definition. But I could see some of the programming I do for pleasure to fall within that category. Correctness is nice I guess, but fitness for purpose was never a consideration in my hobby programming, quite the opposite, actually.
I was programming for a long time before I ever starting programming for money, which is why I feel strongly about this distinction, but it may be that in the current day and age, I am just being a boomer about the whole thing.
reply