That's a good question, one I thought of, but have put off grappling with.
Based on what LLMs have given me for answers so far, I'd look harder for the human-written source of the nroff code. I have written what I believe to be the only quine in the GPP macro processing language, LLMs only refer me to my code if I ask for a GPP quine. Google, Meta, OpenAI really have strip mined the entire web.
If I genuinely thought anything creative or new appeared, I'd probably be at a loss as well.
It should be possible. nroff macro language has looping, string interpolation, functions and if/then/else. That macro language should be turing complete. People have written file-infecting virus malware with it, I believe, which indicate that a quine should be possible. I personally have made several attempts at an nroff quine over the years with no success.
If it's not possible, I'd love to see an explanation, so that task can quite weighing on me.
Turing machine program states are conventionally represented with letters: A, B, C, etc. The starting state is A.
Now suppose you are running a Turing machine program from the beginning. The only state it has visited so far is state A. It runs until it reaches a state that has not been visited yet. What state is it? B? C? D? According to "tree normal form", the name for that next state just is the earliest unused state name, which in this case is B.
Visited states so far are A and B. Run until an unvisited state is reached again. What is its name? C? D? E? Tree normal form says that the state will be called C. And the next newly visited state will be D, etc. In general, the canonical form for a Turing machine program will be the one that puts initial state visits in alphabetical order. (This concept also applies to the multi-color case.)
It's not possible to tell of an arbitrary TM program whether or not that program is in tree normal form. But this proof doesn't look at arbitrary programs. It generates candidate programs by tracing out the normal tree starting from the root, thereby bypassing non-normal programs altogether.
> The most insane python feature is that for loops keep their intermediate variable.
"Insane feature" is a generous way of describing this behavior. I would say it is just a stupid bug that has managed to persist. Probably it is impossible to fix now because of https://xkcd.com/1172/
How typecheckers and linters should deal with this is a tricky question. There is how the language ought to work, and then there is how the language actually does in fact work, and unfortunately they are not the same.
Great post. Some general takeaways for people who want Knuth checks:
1. You are unlikely to find errors in the algorithms themselves, especially if they've been officially published. You might find some infelicities, but these are not counted as full errors. For example, the author here found some confusing-but-not-wrong comments about local variables and unused registers. These are counted as "suggestions" (worth 0x20¢) rather than "errors" (worth 0x$1.00).
2. Knuth is pretty generous with credit -- if your suggestion leads him to find an error, you get credit for the error. The author here said that some defined variables went unused. Knuth pointed out that those variables were in fact used in an exercise. However, in looking this up he noticed a variable-related error in that exercise. Author is credited with 0x$1.00!
3. Exercises are more likely to contain errors and infelicities than the main text. And there are an awful lot of exercises.
4. Knuth includes a whole bunch of stuff in his books that is not related to CS. Lots of weird trivia and references. This stuff is more likely to be wrong than the main text. For example, Knuth mentions "icosahedral objects inscribed with Greek letters" and includes a reference to an article in the Bulletin de l’Institut français du Caire. But the author points out that the article is actually in the Bulletin de l’Institut français d’archéologie orientale. Whoops! 0x$1.00 for you!
I found an error in a published version of TAOCP, 2nd edition I think, with improvements on Sieve of Eratosthenes. I was so excited, then found it was already listed in the errata.
I later got a check for identifying a minor issue with the early history of superimposed coding. I happen to have copies of the relevant patent case containing examples predating Mooers' randomized superimposed coding.
("Happened to" because I had visited the Mooers archive at the Charles Babbage Institute in Minnesota to research some of the early history of chemical information management. Mooers is one of the "fathers" of information retrieval, and in fact coined the term "information retrieval" at a chemistry conference.)
Knuth has the Pablo Picasso’s Dinner Bill “Problem” and so can afford to be generous.
Picasso used to dine and dash as it were by drawing a doodle on the back of his check when the bill came due, and often enough the owner would choose to frame the check instead of cash it.
For a long time most of the cost of writing checks to Knuth is the writing of the checks, not the cashing of them. He’s paying for X00 checks at a time and the energy to fill them out. And anyone who had gotten their first check from him would not cash it.
Though these days I can cash a check via a phone app and so I don’t need to forfeit the check to get the money.
My bank knows where I live and if I start cashing fake checks they know where to send the FBI.
Even in the old paper days banks where a little hesitant to let me come into your bank with a check from you and cash it without me also being a customer of that same bank. In theory you could do it, but sometimes if you tried you got the runaround.
You can still wreak havoc with the account and routing numbers if you want to - it gets reversed when noticed which is why people still do it - sometimes it’s not noticed.
> ... includes a reference to an article in the Bulletin de l’Institut français du Caire. But the author points out that the article is actually in the Bulletin de l’Institut français d’archéologie orientale. Whoops! 0x$1.00 for you!
I do wonder whether this is more likely to be a case where the journal actually changed its name over time (perhaps because the Institut itself did) and then made the older papers available under the new name - which would mean both references are ultimately correct.
I don't think so. The article links to an image of the 1930 journal article – https://gallica.bnf.fr/ark:/12148/btv1b53180372w – it doesn't look like it has been republished, it looks like something close to the original publication
I don't completely know what is going on here, but I guess it is something like this: the institute has since 1898 officially been called Institut français d'archéologie orientale, and its journal has always officially been called Bulletin de l'Institut français d'archéologie orientale. However, historically, people would sometimes add du Caire (in Cairo) to the institute's name (to specify its location) – this habit was supported by the history that, prior to 1898, the institute (or its predecessor) was called École française du Caire (French School of Cairo) – and then unofficially abbreviate it from Institut français d'archéologie orientale du Caire to Institut français d'archéologie du Caire or even Institut français du Caire. And since the journal is named for the institution, once people got in the habit of unofficially abbreviating the name of the institution, they applied the same habit to the journal.
So Bulletin de l’Institut français d’archéologie orientale has always been the official name of the journal, but Bulletin de l’Institut français du Caire is a historical unofficial alternative name.
As I said, I'm just speculating, I don't really know. But this seems more plausible to me than the journal or institute changing its name, because I can't find any evidence of any name change since 1898, which was long before the publication of the 1930 article.
> You never understand other people's code as well as your own. No matter how thoroughly you've read it, you've only read it, not written it.
There is certainly some truth to this. On the other hand, it's possible to become blinded to defects in code you've written yourself. You see what you intended for the code to do rather than what it actually does. Reading someone else's code, it can be easier to see what's really going on, since you just see what's there.
The comparison with mathematics also makes sense here. It’s much easier to spot typos in other peoples’ work than your own for exactly that reason: when you read back what you wrote, you read back what you meant to write rather than what’s actually there.
Open any textbook (even a fourth edition of a famous one, written by an expert) and you’ll find countless typos. In fact, in a certain sense, the more expert you are the less suitable you are as a proof reader for such books.
One of my undergrad tutors taught complex analysis with a book she had written, and she offered a reward for any one who found an error. She said the best students never claimed the reward, only the people that had to study each word carefully.
You touched on it but I've experienced the same reading code when I knew what was intended by the code, regardless of who wrote it. I miss obvious but simple bugs because I only read what it is mean to do.
I often tell younger engineers that the human brain is the slowest, lowest-memory, and most error-prone runtime for a program. If they're stuck trying to figure out a bug, one of the most effective things they can do is validate their assumptions about what's happening, because there wouldn't be a bug if everything was happening exactly according to expectations.
> wouldn't be a bug if everything was happening exactly according to expectations
This isn't quite true, especially concerning distributed systems. It's relatively common for a software system to be broken by design. It's not that the developer didn't know how to use the programming language to get the computer to do what they want. It's that what the developer wanted reflects a poor model of the world, a logical inconsistency, or just a behavior which is confusing to users.
Keep in mind I said that this is advice I give junior engineers specifically; they shouldn't be the ones responsible for designing distributed systems in the first place. For someone in that part of their career, this advice is meant to help to learn the skills the need to solve the problems they're dealing with, and it's not intended to be universal to all circumstances, just a useful thing to keep i mind.
"a poor model of the world, a logical inconsistency, or just a behavior which is confusing to users"
I expect when I pull from the queue (but it was designed non-atomically) that I will be guaranteed to only grab the item once and only once, even after a failure. That expectation is wrong, but the developer may have implemented their intent perfectly, they just didn't understand that there are error cases they didn't account for.
That’s why I learned to log literally everything into stdout unless a process is time-sensitive and it’s deep production and it passed the mark where bugs and insights occur once a month+ and there’s zero chance someone asking me what exactly happenes with X at Y afternoon-ish last Friday.
The obvious exception are recursive number-fiddling algos which would spam gigabytes of output due to big N.
This way I can just read assumptions and see branches taken and what’s wrong as if it was written in plain text.
When I see klocs without a single log statement, to me it’s readonly and not worth touching. If you’re stuck with a bug, log everything and you’ll see it right there.
Just reduce the time horizon you keep the logs until you can afford it. Also, as he mentioned, once a system is getting bugs infrequently, you can lower the log level. My standard is to have a log msg for each branch in the code. In C, I would use macros to also have a count of all the fmt strings the log package encountered (so I still got a sort of profile of the logic flows encountered, but not have the sprintf expense), but I haven't figured out an efficient way to do that in Go yet (i.e. not using introspection).
This is one of the reasons I think that the push towards server-side UI is misguided. It's much easier to walk through the runtime of a program running locally than it is to step through a render that's distributed across a network.
I was responding to your blanket claim that server-side is misguided in general.
I have no idea what you consider complex, but most people pushing toward server side UI are not advocating that it's a one-stop solution, just that it simplifies a large majority of situations that many of us are in which is building CRUD apps. You can even get pretty complex, like say an email client though at that point we're in a grey area where you could kind of go either way. If we're talking something like building PhotoShop in-browser or even a calendar or gantt chart (which I have worked on) then, no, I would not personally advocate server side and instead use a good client-side view library.
The Elixir/Erlang comment was that it makes server-side even easier as you can hop into running production systems and debug them.
I, for instance, learned years ago to refuse to help people debug promise chains in languages with async-await semantics. Rewrite it and then get back to me if it still doesn’t work. They usually transcribe the intent of the chain and not the letter of it, fixing the bug they can’t see. And if not the error makes enough sense they can figure it out.
I believe that is the source of "the worst code I have seen is code I wrote six months ago" feeling. It has been long enough that the code has fallen out of your memory, making you less blind to the defects. You can now see it as an outsider would see it.
There's some idiom that says something like "you don't understand something if you can't explain it." I think this is the real point of code review. To make a case for why your code is valuable. If it's just a blob of 1000 lines of "refactor" or "adding feature." It means nothing. A good commit has some kind of abstract tailored to what work was done.
Then a review becomes something like "the claim was made this function does this, does it look like the function does what it says it does?" If you can understand it in context, then you've added trust and knowledge of how it works.
However it seems to often be the case a review turns into "I like this word better than that word" so back to the explaining it point, it becomes a bit of a self review with the hope it might be helpful to somebody else in the future.
There is a balance there. You can have 1000 trivial commits making it hard to see the fact you just have 10 features, and ten mind-breaking commits making it hard to see what each feature entails. (Then there's the 50 commits "try this" where you are fighting CI or CD and can't test without going thru a git commit/push.)
For anyone who finds this sort of discussion interesting, I highly recommend reading Chapter 3 of The Structure and Interpretation of Computer Programs. There is quite a bit of philosophical musing about the connection between time and state. For example:
> The basic phenomenon here is that synchronizing different processes, establishing shared state, or imposing an order on events requires communication among the processes. In essence, any notion of time in concurrency control must be intimately tied to communication. It is intriguing that a similar connection between time and communication also arises in the Theory of Relativity, where the speed of light (the fastest signal that can be used to synchronize events) is a fundamental constant relating time and space. The complexities we encounter in dealing with time and state in our computational models may in fact mirror a fundamental complexity of the physical universe.
Thanks for sharing that. Einstein and Minkowski knew perfectly well that space and time are inseparable over 100 years ago.
>Not sure what religion has to do with it.
>absolute time is not falsifiable…you have to believe in it.
Either time is a necessary condition for experience, or it’s a measurable object of experience. It can’t be both. You can’t have time in time. Which one do you believe in?
Are you saying: Lets say a Theory of Everything is found, it explains Relativity, and Quantum Mechanics, Gravity, . Everything is tied together. And, this theory does not have Time. Time is just a side effect we can measure. But, at that point, you would still need some 'belief'? Because it is so far beyond our ability to perceive it maybe?
Or, another way. Even a Theory of Everything, at it's root is just Math, it isn't 'what the universe actually is'. So we'd still need belief?
Looking at some of these screenshots, this is such a fantastic idea that it is surprising it wasn't done earlier. Magit has the perfect interface, why shouldn't everything be like that? Who wants to read the Info manual anyway? Info, Dired, Calc, obviously. Org mode, of course.
Maybe other people have been working separately on similar ideas? Really it is extremely obvious in retrospect that everything should have a Magit style interface. Ultimately, it would be great if that were the default, generally assumed style throughout Emacs. It would be great if all that effort could be coordinated and merged into the main distribution. Built-in, no external packages.
Well said. The Magit interface is such a pleasure to use, sometimes I will modify and rearrange commits just for fun. A Magit-style interface for Calc seems like a great idea.
> We are selecting for the maximally pathological machines.
This is not quite right. We're selecting for maximally long-running (or mark-producing, etc) programs. Whether those programs turn out to be "pathological" in some sense is an open question, and a question that can only be answered (to a limited extent) by observing and analyzing actual champion machines. Apriori, there's nothing we can say about what a champion program will do or what its code will be like. The Spaghetti Code Conjecture says that these champion programs will tend to be complicated. I have argued in the past that this may not be the case. It's entirely possible that the code of a champion program will be written more or less straightforwardly, and its long-running-ness comes from executing some bizarre and exotic mathematical function.
Actually, I think the more likely outcome is that some champions will be spaghetti and some will be clean. If they were all spaghetti or all clean, then that fact could be exploited to speed up the search process by discarding all programs not matching the property. And that sounds too good to be true, so it probably isn't. Maybe BB(8) champion is clean, BB(9) is spaghetti, etc, and there are no reliable trends.
reply