Niklaus Wirth has died
twitter.com
twitter.com
That led him to quip, "In Europe I'm called by name, but in the US I'm called by value."
https://en.wikiquote.org/wiki/Niklaus_Wirth
https://lists.racket-lang.org/users/archive/2014-July/063519...
> Finally a short story for the record. In 1968, the Communications of the ACM published a text of mine under the title "The goto statement considered harmful", which in later years would be most frequently referenced, regrettably, however, often by authors who had seen no more of it than its title, which became a cornerstone of my fame by becoming a template: we would see all sorts of articles under the title "X considered harmful" for almost any X, including one titled "Dijkstra considered harmful". But what had happened? I had submitted a paper under the title "A case against the goto statement", which, in order to speed up its publication, the editor had changed into a "letter to the Editor", and in the process he had given it a new title of his own invention! The editor was Niklaus Wirth.
It is refreshing to see the old-fashioned trope of the genius computer scientist / software enginieer as a "foreigner to the world" being contested again and again by stories like this.
Of course people like Niklaus Wirth are exceptional in many ways, so it might be that the trope has/had some grain of truth, that just does not co-correlate with the success of said person :)
And of course people might want to argue about the differences betweem SE, CS and economics.
After all that rambling... RIP and thank you Niklaus!
We should take them away.
"Wit" is just wrong. Perhaps that was a joke that I missed about the man's humour.
He was Swiss, more exactly from the city of Winterthur located in the canton (state) of Zürich. The canton's official language is German, however. Of course, people over there speak in a strong local dialect called "Züritüütsch".
> a strong local dialect called "Züritüütsch".
Damn, I've never seen a word with three u-umlauts in it. How the hell do you pronounce two consecutive u-umlauts? "eu-eu"?
Wirth lived in the United States for some time throughout his life but is a Zürich native. He must have spoken Züritüütsch („Zurich German“) privately, I am pretty sure (without having known him personally).
https://news.ycombinator.com/item?id=15361069
at the end of the comment.
It reminds me of my only meeting with Andy Tanenbaum / AAT [0], one of the smartest, nicest computer science guy I've ever met in my life. I can't recall the many puns and jokes he shared, but it was just incredible.
Joe would often quote Wirth as saying that yes, overlapping windows might be better than tiled ones, but not better enough to justify their cost in implementation complexity.
RIP. He is also a hero for me for his 80th birthday symposium at ETH where he showed off his new port of Oberon to a homebrew CPU running on a random FPGA dev board with USB peropherals. My ambition is to be that kind of 80 year old one day, too.
Wirth was such a legend on this particular aspect. His stance on compiler optimizations is another example: only add optimization passes if they improve the compiler's self-compilation time.
Oberon also, (and also deliberately) only supported cooperative multitasking.
It just renamed itself to asynchronous programing. That's quite literally what an 'await' is.
i think it's safe to say that the number of personal computers running operating systems without preemptive multitasking is now vanishingly small
as i remember it, oberon didn't support either async/await or cooperative multitasking. rather, the operating system used an event loop, like a web page before the introduction of web workers. you couldn't suspend a task; you could only schedule more work for later
for (;;) {
int r = GetMessage(&msg, NULL, 0, 0);
if (!r) break;
if (r == -1) croak();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
or, in yeso, for (;;) {
yw_wait(w, 0);
for (yw_event *ev; (ev = yw_get_event(w));) handle_event(ev);
redraw(w);
}
async/await doesn't always hide the event loop in that sense; python asyncio, for example, has a lot of ways to invoke the event loop or parts of it explicitly, which is often necessary for integration with software not written with asyncio in mind. i used to maintain an asyncio cubesat csp protocol stack where we had to do thisto some extent, though, this vitiates the concurrency guarantees you can otherwise get out of async/await. software maintainability comes from knowing that certain things are impossible, and pure async/await can make concurrency guarantees which disappear when a non-async function can invoke the event loop in this way. so i would argue that it goes further than just hiding the event loop. it's like saying that garbage collection is about hiding memory addresses: sort of true, but false in an important sense
The key thing about 2023-era asynchronous versus 1995-era cooperative multitasking is code readability and conciseness.
Under the hood, I'm expressing the same thing, but Windows 3.1 code was not fun to write. Python / JavaScript, once you wrap your head around it, is. The new semantics are very readable, and rapidly improving too. The old ones were impossible to make readable.
You could argue that it's just syntactic sugar, but it's bloody important syntactic sugar.
That's my point, we still do that. And based on your phrasing we're forgetting it :)
possibly you are using the terms in subtly different ways so it appears that we disagree when we do not
If the OS forcefully switches control it's preemptive.
in the mac os 8 documentation, explaining how mac os 8 only has 'cooperative multitasking', the term is defined in the way i'm using it (https://developer.apple.com/library/archive/documentation/Ca...):
> In programming, a task is simply an independent execution path. On a computer, the system software can handle multiple tasks, which may be applications or even smaller units of execution. For example, the system may execute multiple applications, and each application may have independently executing tasks within it. Each such task has its own stack and register set.
> Multitasking may be either cooperative or preemptive. Cooperative multitasking requires that each task voluntarily give up control so that other tasks can execute. (...)
> The Mac OS 8 operating system implements cooperative multitasking between applications. The Process Manager can keep track of the actions of several applications. However, each application must voluntarily yield its processor time in order for another application to gain it. An application does so by calling WaitNextEvent, which cedes control of the processor until an event occurs that requires the application’s attention.
that is, this requirement that each task have its own stack is not just something i made up; it's been part of common usage for decades, at least in some communities. the particular relevant distinction here is that, because each task has its own stack (or equivalent in something like scheme), multitasking doesn't require restructuring your code, because calling a normal function can yield the cpu. in the specific case of macos this was necessary so that switcher/multifinder/process-manager could multitask mac apps written for previous versions of macos that didn't have multitasking
what term would you propose for what i'm calling 'cooperative multitasking', like forth and mac os 8 and windows 3.1 (https://softwareengineering.stackexchange.com/questions/3507... https://retrocomputing.stackexchange.com/questions/791/how-d...)? this terminology is not absolutely standardized, and i'd be happy to use different terminology in order to be able to communicate productively
also could you please answer my request for clarification in https://news.ycombinator.com/item?id=38861074
Those are implementation details. What's actually happening in all cases is my definition.
> also could you please answer my request for clarification in
Yes, your examples or the 5 million other implementations of event loops. You forgot to add gtk's for example :)
> in the mac os 8 documentation
... and I see no mention of stacks on Wikipedia:
https://en.wikipedia.org/wiki/Cooperative_multitasking
Which lumps in both the explicit event loop style and the syntactic sugar that got added later.
We could throw definitions around till kingdom come like this. And it's not the exact definition that's my problem.
i'll see if i can flesh out the wikipedia article a bit
And why do you think that in the case of an explicit event loop you don't have to yield? You do have to, and have to sort out some way to continue on your own. Which makes the new 'syntactic sugar' approaches much easier of course. Doesn't mean the principle isn't the same and they don't deserve the same name.
Yes, of course you could, since everything beyond, uh, paper tape, next-state table, and current pen-position (or whatever other pieces there are in a theoretical Turing machine) is basically syntactic sugar. Or, IOW, all programming languages higher than assembly are nothing but syntactic sugar. I like syntactic sugar.
(But OTOH, I'm a diabetic. Gotta watch out for that sugar.)
i hear they're concerned about cancer of the semicolon
I don't know if you're intentionally using "colour" to reference https://journal.stuffwithstuff.com/2015/02/01/what-color-is-... ? Cooperative multitasking (which I'd never heard of before) seems from its Wikipedia page to be primarily concerned with Operating System-level operations, whereas that article deals with programming language-level design. Or perhaps they are not distinct from one another in your perspective?
I ask because I've found `async/await` to just be an irritating overhead; a hoop you need to jump through in order to achieve what you clearly wanted to do all along. You write (pseudocode) `var foo = myFunction()`, and (depending on your language of choice) you either get a compilation or a runtime error reminding you that what you really meant was `var foo = await myFunction()`. By contrast, a design where every function is synchronous (which, I'd guess, more closely matches most people intuition) can implement async behaviour when (rarely) desired by explicitly passing function invocations to an Executor (e.g. https://www.digitalocean.com/community/tutorials/how-to-use-...). I'd be curious to hear what advantages I'm missing out on! Is it that async behaviour is desired more-often in other problem areas I don't work in, or that there's some efficiency provided by async/await that Executors cannot provide, or something else?
yes! i'm referencing that specific rant. except that what munificent sees as a disadvantage i see as an advantage
there's a lot of flexibility in systems design to move things between operating systems and programming languages. dan ingalls in 01981 takes an extreme position in 'design principles behind smalltalk' https://www.cs.virginia.edu/~evans/cs655/readings/smalltalk....
> An operating system is a collection of things that don't fit into a language. There shouldn't be one.
in the other direction, tymshare and key logic's operating system 'keykos' was largely designed, norm hardy said, with concepts from sigplan, the acm sig on programming languages, rather than sigsosp
sometimes irritating overhead hoops you need to jump through have the advantage of making your code easier to debug later. this is (i would argue, munificent would disagree) one of those times, and i'll explain the argument why below
in `var foo = await my_function()` usually if my_function is async that's because it can't compute foo immediately; the reasons in the examples in the tutorial you linked are making web requests (where you don't know the response code until the remote server sends it) and reading data from files (where you may have to wait on a disk or a networked fileserver). if all your functions are synchronous, you don't have threads, and you can't afford to tie up your entire program (or computer) waiting on the result, you have to do something like changing my_function to return a promise, and putting the code below the line `var foo = await my_function()` into a separate subroutine, probably a nested closure, which you pass to the promise's `then` method. this means you can't use structured control flow like statement sequencing and while loops to go through a series of such steps, the way you can with threads or async
so what if you use threads? the example you linked says to use threads! i think it's a widely accepted opinion now (though certainly not universal) that shared-mutable-memory threading is the wrong default, because race conditions in multithreaded programs with implicitly shared mutable memory are hard to detect and prevent, and also hard to debug. you need some kind of synchronization between the threads, and if you use semaphores or locks like most people do, you also get deadlocks, which are hard to prevent or statically detect but easy to debug once they happen
async/await guarantees you won't have deadlocks (because you don't have locks) and also makes race conditions much rarer and relatively easy to detect and prevent. mark s. miller, one of the main designers of recent versions of ecmascript, wrote his doctoral dissertation largely about this in 02006 http://www.erights.org/talks/thesis/index.html after several years working on an earlier programming language called e based on promises like the ones he later added to js; but i have to admit that, while i've read a lot of his previous work, i haven't read his dissertation yet
cooperative multitasking is in an in-between place; it often doesn't use locks and makes race conditions somewhat rarer and easier to detect and prevent than preemptive multitasking, because most functions you call are guaranteed not to yield control to another thread. you just have to remember which ones those are, and sometimes it changes even though your code didn't change
(in oberon, at least the versions i've read about, there was no way to yield control. you just had to finish executing and return, like in js in a web page before web workers, as i think i said upthread)
that's why i think it's better to have colored functions even though it sometimes requires annoying hoop-jumping
You will get them in .NET and C++, because they map to real threads being shared across tasks.
There is even a FAQ maintained by .NET team regarding gotchas like not calling ConfigureAwaitable with the right thread context in some cases where it needs to be explicitly configured, like a task moving between foreground and background threads.
Then what you want are coroutines[1], which are strictly more flexible than async/await. Languages like Lua and Squirrel have coroutines. I and plenty of other people thing it's tragic that Python and Javascripts added async/await instead, but I assume the reason wasn't to make them easier to reason about, but rather to make them easier to implement without hacks in existing language interpreters not designed around them. Though Stackless Python is a CPython fork that adds real coroutines, also available as the greenlet module in standard CPython [2], amazing that it works.
[1] Real coroutines, not what Python calls "coroutines with async syntax". See also nearby comment about coroutines vs coop multitasking https://news.ycombinator.com/item?id=38859828
We used coroutines in our interrupt rich environment in our real time medical application way back when. This was all in assembly language and the coroutines vastly reduced our multithreading errors to effectively zero. This is one place where C , claimed to be close to the machine falls down.
how did coroutines reduce your multithreading errors
A core problem is that it's now clear most apps have hundreds or thousands of little tasks going, increasingly bound by network, IO, and similar. Async gives nice semantics for implementing cooperative multitasking, without introducing nearly as many thread coherency issues as preemptive.
I can do things atomically. Yay! Code literally cooperates better. I don't have the messy semantics of a Windows 3.1 event loop. I suspect it will take over more and more into all walks of code.
Other models are better for either:
- Highly parallel compute-bound code (where SIMD/MIMD/CUDA-style models are king)
- Highly independent code, such as separate apps, where there are no issues around cooperation. Here, putting each task on a core, and then preemptive, obviously wins.
What's interesting is all three are widely used on my system. My tongue-in-cheek comment about cooperative multitasking winning was only a little bit wrong. It didn't quite win in the sense of taking over other models, but it's in widespread use now. If code needs to cooperate, async sure beats semaphores, mutexes, and all that jazz.
[1]: Asynchronous programming is not the only form of cooperative programming. Usually cooperative multi-tasking systems have a special system call yield() which gives up the processor in addition to io induced context-switches.
Assuming your type isn't an Awaitable, with magic methods to influence how the compiler actually generates the state machine.
Is this the same as coroutines as in Knuth's TAOCP volume 1?
Sorry, my knowledge is weak in this area.
i could just be wrong tho
lua's coroutines aren't automatically scheduled (there isn't a built-in run queue) but explicitly resumed, which is a difference from the usual cooperative-multitasking systems; arguably on that basis you could claim that they aren't quite 'cooperative multitasking' on their own
the last time i implemented a simple round-robin scheduler for cooperative multitasking was in july, as an exercise, and it was in arm assembly language rather than lua. it was 32 machine instructions and 64 lines of code (http://canonical.org/~kragen/sw/dev3/monokokko.S), plus 14 lines of example code to run in the threads. when i went to go look at that just now i was hoping to come up with some kind of crisp statement about the relative importance or complexity of the stack-switching functionality and the run-queue maintenance facility, but in fact there isn't a clear separation between them, and that version of the code creates all the tasks at assembly time instead of runtime. a more flexible version with start, spawn, yield, and exit calls, which respects the eabi so you can write your task code in c (http://canonical.org/~kragen/sw/dev3/einkornix.h et seq.), is 53 lines of assembly and 34 machine instructions, but similarly has no real separation of the two concerns
Right, I think this is where I am coming from. Generators, for example, can be implemented via coroutines, but I would not call a generator "cooperative multitasking."
That's very cool! Yeah, I have never done this myself, but in my understanding implementations in assembly can be very small.
> when i went to go look at that just now i was hoping to come up with some kind of crisp statement about the relative importance or complexity of the stack-switching functionality and the run-queue maintenance facility, but in fact there isn't a clear separation between them
That's fair, but I don't think that's the final say here, as you were building a system for cooperative multitasking explicitly, with no reason to try and separate the concerns. When a system is very simple, there's much less reason for separation.
Actually, this makes me realize why I probably have this bias for thinking of them separately: async/await in Rust. The syntax purely creates a generator, it is totally inert. You have to bring along your own executor (which contains a scheduler among other things). Separating the two cleanly was an explicit design goal.
it certainly isn't the final say! it's just an analysis of how my own code turned out, not any kind of universal lesson
the implementation in monokokko, which reserves the r10 register to always point to the currently running task, is five instructions
.thumb_func
yield: push {r4-r9, r11, lr} @ save all callee-saved regs except r10
str sp, [r10], #4 @ save stack pointer in current task
ldr r10, [r10] @ load pointer to next task
ldr sp, [r10] @ switch to next task's stack
pop {r4-r9, r11, pc} @ return into yielded context there
interestingly, what you say of rust's generators is also sort of true of monokokko> The syntax purely creates a generator, it is totally inert. You have to bring along your own executor (which contains a scheduler among other things).
the above five instructions, or arguably just ldr r10, [r10], is the executor. the in-memory task object consists of the saved stack pointer, the link to the following task, and then whatever variables you have in thread-local storage. but from a different point of view you could say that the in-memory task object consists of the saved stack pointer, a pointer to executor-specific status information (which for this executor is the following task, or conceptually the linked list of all tasks), and then other thread-local variables. i think the difference if you were to implement this same executor with rust generators is just that you probably wouldn't make the linked list of all tasks an intrusive list?
> i think the difference if you were to implement this same executor with rust generators is just that you probably wouldn't make the linked list of all tasks an intrusive list?
You still could, and IIRC tokio uses an intrusive linked list to keep track of tasks. There's no specific requirements for how you keep track of tasks, or even a standardized API for executors, which is why you'll hear people talk about why they want "to be generic over runtimes" and similar.
Your opinion vs. my opinion, obviously. But the user reports of the experience in Rust is hardly even close to unanimous praise and I still say it's a mistake to sit down with an empty Rust program and immediately reach for "async" without considering whether you actually need it. Even in the network world, juggling hundreds of thousands of simultaneous tasks is the exception rather than the rule.
Moreover, cooperative multitasking was given up at the OS level for good and sufficient reasons that I see no evidence that the current thrust in that direction has solved. As you scale up, the odds of something jamming your cooperative loop monotonically increase. At best we've increased the scaling factors, and even that just may be an effect of faster computers rather than better solutions.
i suspect that this will end up being the paradigm that wins out, even though it isn't popular today
Since I've given up on monetizing this project, I may as well just link to its current state (which is very rough, the STM API described in the website is only partly implemented, and there's lots of cruft from its previous life that I haven't ripped out yet). Note that this is a fork of the previous (now MIT-licensed) Gaia programming platform (https://gaia-platform.github.io/gaia-platform-docs.io/index....).
https://github.com/senderista/nextdb/tree/main/production/db...
The version of this code previously released under the Gaia programming platform is here: https://github.com/gaia-platform/GaiaPlatform/blob/main/prod.... (Note that this predates my removal of IPC from the transaction critical path, so it's about 100x slower.) A design doc from the very beginning of my work on the project that explains the client-server protocol is here (but completely outdated; IPC is no longer used for anything but session open and failure detection): https://github.com/gaia-platform/GaiaPlatform/blob/main/prod....
I read that as octal; so 1024 in decimal. Not a very interesting year, according to Wikipedia.
So sometime between "02000" and "02999"?
Async only allows you to say “run foo now until it has data” to the JavaScript runtime.
IMO, async/await in JavaScript are more like one shot coroutines, not cooperative multitasking.
Having said that, the JavaScript event loop is doing cooperative multitasking (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Even...)
What an elegant metric! Condensing a multivariate optimisation between compiler execution speed and compiler codebase complexity into a single self-contained meta-metric is (aptly) pleasingly simple.
I'd be interested to know how the self-build times of other compilers have changed by release (obviously pretty safe to say, generally increasing).
That said, even if the exact heuristic Wirth used is no longer tenable, there's still a lot of wisdom in the pragmatic way of thinking that inspired it.
You could try to game the system by combining such a change that slows down compilation with one that compensates for it, though, but I think code reviewers of the time wouldn’t accept that.
In short, compiler is not an ideal representation of the user programs it needs to optimize.
EDIT: typo in the last word but I'm leaving it in for obvious reasons.
But yes, it wouldn’t work well in a general context. For example, auto-vectorization likely doesn’t speed up a compiler much at all, while adding the code to detect where it’s possible will slow it down.
So, that feature never can be added.
On the other hand, may lead to better designs. If, instead, you add language features that make it easier for programmers to write vectorized code, that might end up being easier for programmers. They would have to write more code, but they also would have to guess less whether their code would end up being vectorized.
I think that some of the text in "16.1. General considerations" of "Compiler Construction" are sorta close, but does not say this explicitly.
Wirth also had no compunction about changing the syntax of his languages if it made the compiler simpler. Modula-2 originally allowed undeclared forward references within the same file. When his implementation moved from the original multi pass compilers (e.g. Logitech's compiler had 5 passes: http://www.edm2.com/index.php/Logitech_Modula-2) to a single pass compiler http://sysecol2.ethz.ch/RAMSES/MacMETH.html he simply started requiring that forward references had to be declared (as they used to be in Pascal).
I suspect that Wirth not being particularly considerate of the installed base of his languages, and not very cooperative about participating in standardization efforts (possibly due to burn out from his participation in the Algol 68 process) accounts for the ultimately limited commercial success of Modula-2 & Oberon, and possibly for the decline of Pascal.
Go team member, Robert Griesemer, did his Phd under Mössenböck and Wirth.
A marginally useful optimization pass would not pull its weight when added to the first code base, but could in the second code base because it would optimize the run time spent on all the other marginal optimizations.
Though the compiler would start out closer to the small equilibrium in its initial version, and there might not be a way to incrementally move towards the large equilibrium from there under Wirth's rule.
This was a fantastic talk. https://www.youtube.com/watch?v=EXY78gPMvl0
He had the crowd laughing and cheering, and the audience questions in the end were absolutely excellent.
I think I last watched it during the pandemic and was inspired to pick up reading more about Oberon. A demonstration / talk like that is so much better when the audience are rooting for the presenter to do well.
Sorry, couldn't resist.
I first wrote it as "worthwhile", but then the pun practically fell out of the screen at me.
I love Wirth's work, and not just his languages. Also his stuff like algorithms + data = programs, and stepwise refinement. Like many others here, Pascal was one of my early languages, and I still love it, in the form of Delphi and Free Pascal.
RIP, guruji.
Edited to say guruji instead of guru, because the ji suffix is an honorific in Hindi, although guru is already respectful.
Even before I met him at the university I was programming in Oberon because there was a big crowd of programmers doing Wirth languages on the Amiga.
He will be missed.
https://www.google.com/search?q=lords+of+the+rising+sun
My understanding (please correct me) is that Turbo Pascal on PC was actually Modula2 ?
My very first compiled programming language was Pascal. I got the free "PCQ Pascal" from the Fish disks as I wasn't able to get the C headers from Commodore which I would have needed for doing proper Amiga programming. Likewise later Oberon-A although I don't remember where I got that from.
There were also commercial Modula-2 and Oberon-2 compilers. I just found that the Modula-2 compiler was open sourced some years back. https://m2amiga.claudio.ch/
Aminet has directories for Oberon and Modula-2 related programs: https://aminet.net/dev/obero and https://aminet.net/dev/m2
Undergraduate students were all in awe of him, but I got the impression that he did not particularly enjoy teaching them (Unlike other professors, however, he did not try to delegate that part of his responsibilities to his assistants). He seemed to have a good relationship with his graduate students.
In his class on compiler construction, he seemed more engaged (the students were already a bit more experienced, and he was iterating the Oberon design at the time). I remember an exchange we had at the oral exam — he asked me to solve the "dangling ELSE" problem in Pascal. I proposed resolving the ambiguity through a refinement of the language grammar. He admitted that this would probably work, but thought it excessively complex and wondered where I got that idea, since he definitely had not taught it, so I confessed that I had seen the idea in the "Dragon Book" (sort of the competition to his own textbook). Ultimately, I realized that he just wanted me to change the language to require an explicit END, as he had done in Modula-2 and Oberon.
Socially, he was fun to talk to, had a great store of computer lore, of course. He was also much more tolerant of "heresies" in private than in public, where he came across as somewhat dogmatic. Once, the conversation turned to Perl, which I did not expect him to have anything good to say about. To my surprise, he thought that there was a valid niche for pattern matching / text processing languages (mentioning SNOBOL as an earlier language in this niche).
this is terrible news;
is there a better source than twitter (edit: https://lists.inf.ethz.ch/pipermail/oberon/2024/016856.html thanks to johndoe0815);
wirth was the greatest remaining apostle of simplicity, correctness, and software built for humans to understand; now only hoare and moore remain, and moore seems to have given the reins at greenarrays to a younger generation;
young people may not be aware of the practical, as opposed to academic, significance of his work, so let me point out that begin
the ide as we know it today was born as turbo pascal;
most early macintosh software was written in pascal, including for example macpaint;
robert griesemer, one of the three original designers of golang, was wirth's student and did his doctoral thesis on an extension of oberon, and wirth's languages were also a very conspicuous design inspiration for newsqueak;
tex is written in pascal;
end;
end.
But it is the real account of Bertrand Meyer, creator of the Eiffel language.
but still, it's twitter, liable to vanish or block non-logged-in access at any moment
(admittedly you could make the same criticism of hn; it certainly isn't decentralized and resilient against administrative censorship like usenet was)
I'm actually active on Mastodon but I am thinking about getting on Instagram as well because the content I post that does the best on Mastodon would fit in there.
dang, maybe we can change the url to this instead? this url has been stable for at least 14 years (http://web.archive.org/web/20070720035132/https://lists.inf....) and has a good chance of remaining stable for another 14, while the twitter url is likely to disappear this year or show different results to different people
Also Alan Kay still with us.
alan kay is equally great, but on some axes he is the opposite extreme from wirth: an apostle of flexibility, tolerance for error, and trying things to see what works instead of planning everything out perfectly. as sicp says
> Pascal is for building pyramids—imposing, breathtaking, static structures built by armies pushing heavy blocks into place. Lisp is for building organisms—imposing, breathtaking, dynamic structures built by squads fitting fluctuating myriads of simpler organisms into place.
kay is an ardent admirer of lisp, and smalltalk is even more of an organism language than lisp is
https://www.legacy.com/us/obituaries/venturacountystar/name/...
http://www.canonical.org/~kragen/sw/dev3/meta5ixrun.py
(i mean i would recommend reimplementing it, not using my reimplementation; it takes a few hours or days)
after i wrote it, the acm made all old papers, including schorre's meta-ii paper, available gratis; they have announced that they also plan to make them open-access, but so far have not. still, this is a boon if you want to do this. the paper is quite readable and is at https://dl.acm.org/doi/10.1145/800257.808896
I haven't reimplemented meta-ii, I will.
You might like https://old.reddit.com/r/rust/comments/18wnqqt/piccolo_stack...
i hope they do go to open access; these papers are too valuable to be lost to future acm management or bankruptcy
- indexing from 1 instead of 0;
- the absence of a nilness/nonexistence distinction (so misspelling a variable or .property silently gives the wrong answer instead of an exception);
- variables being global by default, rather than local by default or requiring an explicit declaration;
- printing tables by default (with %q for example) as identities rather than contents. (you could argue that this is a simplicity thing; lua 5.2 is under 15000 lines of code, which is pretty small for such a full-featured language, barely bigger than original-awk at 6200 lines of c and yacc plus 1500 lines of awk, and smaller than mawk at 16000 lines of c, 1100 lines of yacc, and 700 lines of awk. but a recursive table print function with a depth limit is about 25 lines of code.)
none of these are fatal flaws, but with the benefit of experience they all seem like clear mistakes
It's been long time since I last used lua and only positive memories remained :) I used it for adding scripting in the apps I worked on and the experience was very good -- sandboxed from the start, decent performance.
Perhaps having 0-based indexes would've been bad for our users but I don't think they used arrays at all.
'.SYNTAX' .ID .OUT('ADR' *) ...
but I'm having trouble understanding what the ADR code is supposed to do. By my understanding, that line should instead read something like '.SYNTAX' .ID .OUT('CLL *') .OUT('HLT') ...
where HLT is some code that causes the machine to halt, or possibly '.SYNTAX' .ID .OUT('B *') ...
using the otherwise-unused unconditional branch code, and then defining R on an empty call stack as a machine halt.i agree, it would make much more sense to say
'.syntax' .id .out('cll ' *) .out('hlt')
and thus eliminate the otherwise-unused adr, or simply to put the main production of the grammar at the beginning of the grammar, which is what i did in meta5ix. i think they do define 'r' on an empty call stack as a machine halt, btwthey do mention this startup thing a bit in the text of the paper (p. d1.3-3)
> The first thing in any META II machine program is the address of the first instruction. During the initialization for the interpreter, this address is placed into the instruction counter.
so i think the idea is that their 'binary executable format' consists of the address of the entry point, followed by all the code, and the loader looks at the first word to see where to start running the code. this sounds stupid (because why wouldn't you just start running it at the beginning?) but elf, a.out, and pe all have similar features to allow you to set the entry point to somewhere in the middle of the executable code, which means you have total freedom in how you order the object files you're linking. so even though it's maybe unnecessary complexity in this context, it's well-established practice even 60 years later, and maybe it already was at the time, i don't know
i hope this is helpful! also i hope it's correct, but if not i hope it's at least helpful :)
but i'm sure he'd agree his achievements are not in the same league as wirth's
And yet far from the last. Simple, correct, and beautiful software is still being made today. Most of it goes unnoticed, its quiet song drowned out by the cacophony of attention-seeking, complex, brittle behemoths that top the charts.
That song never faded, you just need to tune in.
fabrice bellard is wirth-level, it's true. not sure about tedu and jcs, because i'm not familiar enough with their work. it's absurd to compare most of the others to wirth and hoare
you're comparing kindergarten finger paintings to da vinci
you said wirth was "far from the last" apostle of simplicity. definition of apostle: https://en.wikipedia.org/wiki/Apostle
You're the one trying to directly compare achievement, not me. If you're looking for top achievers, I'd have to name PHP or systemd, and THAT would be out of place ;)
I even said "in no particular order", because I don't think any two can be easily compared.
My main criterion for inclusion was the drive for simplifying technology, and publishing these efforts:
> An apostle [...], in its literal sense, is an emissary. The word is [...] literally "one who is sent off" [...]. The purpose of such sending off is usually to convey a message, and thus "messenger" is a common alternative translation.
Every single project, person, or community I've named here has some form of web page, blog, RSS feed, papers/presentations, and/or source code, that serve to carry their messages.
Achievement can be measured, simplicity can only be appreciated.
btw, uxn is absolutely the exemplification of "software built for humans to understand" and simplicity. I mean...
> the resulting programs are succinct and translate well to pen & paper computing.
> to make any one program available on a new platform, the emulator is the only piece of code that will need to be modified, which is explicitly designed to be easily implemented
https://100r.co/site/uxn_design.html
how one can frame this as trivial is beyond me.
the bit you quote about uxn having a standard virtual machine to permit easy ports to new platforms is from wirth's 01965 paper on euler http://pascal.hansotten.com/niklaus-wirth/euler-2/; it isn't something devine and rek invented, and it may not have been something wirth invented either. schorre's 01963 paper on meta-ii targets a machine-independent 'fictitious machine' but it's not turing-complete and it's not clear if he intended it to be implemented by interpretation rather than, say, assembler macros
i suggest that if you develop more tolerance for opinions that differ from your own, instead of deprecating them as 'persistent negativity' and 'dictating', you will learn more rapidly, because other people know things you don't, and sometimes that is why our opinions differ. sometimes those things we know that you don't are even correct
i think this is one of those cases. what i said, that you were disagreeing with, was that uxn was not the same kind of achievement as oberon, pascal, quicksort, forth, and structured programming (and, let me clarify, a much less significant achievement) and that it is a long way from [meriting] a turing award. i don't see how anyone could possibly disagree with that, or gloss it as 'uxn is trivial', as you did, unless they don't know what those things are
i am pretty sure that if you ask devine what he thinks about this comment, you will find that he agrees with every word in it
Dewey Schorre and Meta II, eh? Who remembers such things? Well, I do, as I was involved with an implementation of Meta V when I was on the staff of the UCLA Comp Sci dept in 1969.
i recently reimplemented meta-ii myself with some tweaks, http://canonical.org/~kragen/sw/dev3/meta5ixrun.py
i guess i should write it up
it's interesting to see programming language advancement cited as a major contribution to the development of a network protocol
I don't need to "develop more tolerance for differing opinions" - I have no problem with them and am completely open to them, even from people who I feel are communicating in an unfriendly, patronizing or gatekeeping manner. rollcat shared some other people and projects and you took it upon yourself to shoot down as much as possible in that comment - for what purpose? No one said Drecker is "on Wirth's level" when it comes to programming. We don't need him to write FizzBuzz, let alone any other software. I'm sorry you don't recognize the value of a publication like Low-Tech Magazine, but the rest of us can, and your need to shoot down that recognition is why I called your messages persistently negative.
Further, when I give kudos to uxn and recognize it as a cool piece of software, there's absolutely no point in coming in and saying "yeah but it's no big deal compared to ____" , as if anyone was interested in some kind of software achievement pissing contest. The sanctity and reverence for your software idols is not diluted nor detracted from by acknowledging, recognizing and celebrating newer contributors to the world of computing and software.
I have to come back and edit this and just reiterate: All I originally said was "uxn ftw" and you found it necessary to "put me in my place" about something I didn't even say/assert, and make it into some kind of competition or gatekeeping situation. Let people enjoy things. And now, minimizing this thread and never looking at it again.
Someone sent me this thread so I could answer, and I do agree. I for one think uxn is trivial, it was directly inspired by the VM running Another World and created to address a similar need. It's not especially fast, or welcoming to non-programmers, it was a way for my partner and I to keep participating in this fantastic universe that is software development, even once our access to reliable hardware was becoming uncertain. It's meant to be approachable to people in a similar situation and related interests, and possibly inspire people to look into assembly and stack machines -- but it has no lofty goals beyond that. We're humbled that it may have inspired a handful of developers to consider what a virtual machine designed to tackle their own needs might look like.
A lot of our work is owed to Wirth's fantastic documentation on Oberon, to the p-machine and to pascal. Niklaus' works has influenced us in ways that it would be very unlikely that we could pass forward. I'm sad to hear of Nicklaus' passing, there are people who inspire me in similar ways, that are alive today and that I look up to for inspiration, but to me, Wirth's work will remain irreplaceable. :)
-- Devine
“The term [Apostle] is also used to refer to someone who is a strong supporter of something.[5][6]“
So I would call many people and myself (as someone who started studying Computer Science with Assembler and Modula-2) Apostle of simplicity.
No need for techno-classism.
Several research groups continued work on L4 after Liedtke's death (Hermann Härtig in Dresden, Gernot Heiser in Sydney, a bit of research at Frank Bellosa's group in Karlsruhe and more industrial research on L4 for embedded/RT systems by Robert Kaiser, later a professor in Wiesbaden), but I would still argue that Liedtke's original work was the most influential, though all the formal verification work in Sydney also had significant impact - but that was only enabled by the simplicity of the underlying microkernel concepts and implementations.
because i've also written a terminal emulator, and it compiles from source in 0.98 seconds https://gitlab.com/kragen/bubbleos/blob/master/yeso/admu-she... screenshot of running vi in it at https://gitlab.com/kragen/bubbleos/blob/master/yeso/admu_she...
(steps to reproduce: install dependencies; make; rm admu-shell admu-shell-fb admu-shell-wercam admu admu.o admu-shell.o admu_tv_typewriter.o; time make -j. it only takes 0.37 seconds if you only make -j admu-shell and don't build the other executables. measured on debian 12.1 on a ryzen 5 3500u)
i wrote pretty much all of it on october 12 and 13 of 02018 so forgive me if i don't think that writing a terminal emulator without scrollback is an achievement comparable to pascal and oberon etc.
not even if it were as great as st (and actually admu sucks more than st, but it can still run vi and screen)
WRT to the scrollback it seems like they're going overboard in being difficult about features that adds little code but has much impact, while not paying close attention to their dependencies. Things they could've done without (the copy on my system includes libz, libpng, libexpat (assuming for fontconfig, which is itself a giant steaming pile of excessive complexity), and even libbrotlicommon... I'm pretty sure I have no brotli images on my system that st has any business touching...
I used st until I replaced it with my own, and I can't fault it for many things in terms of usability, though. Other than the box drawing - it's not pixel perfect (I only bring that up because I bikeshedded a pixel-perfect override for the boxdrawing characters for my font renderer when I was bored a while back so it's the one place where I can crow about mine being better than st ;) - in every other area it still has warts to clean up)
it would be interesting to see what an even more minimalist and more usable terminal emulator looked like. both your work and st are constrained by having to support terminal control languages with a lousy strength to weight ratio, something oberon opted out of
That is one area where st's "tmux copout" on scroll somewhat makes sense - it would be a reasonable option to define a clean sufficient subset that lets you run enough stuff to tell people to just run anything that breaks under tmux/screen or a separate filter.
But from what I see with terminals, there's a lot of reluctance to do this not because people believe all these codes are so important but because it seems to become a bit of a matter of pride to be a precise as possible. I admit to having succumbed to a few myself, like support for double-width and double-height characters, as well as "correct" (bright/dim rather than on/off) blink and support for the nearly unsupported rapid blink... There is also a pair of escape codes to enable and disable fraktur. This is fertile ground for procrastinating terminal developers to implement features used by one person in the 70's sometime.
At the same time I sometime catch myself hoping some of these features will be used more... A very few I'll probably add support for because I want to use them in my text editor, e.g. different coloured underlines, and squiggly underlines are both easy to do and actually useful..
I think with a cleaner set of control codes, though, you could certainly fit quite a few of those features and still reduce the line count significantly...
i've been thinking that maybe nested tables would be better than character cells, for example, accommodating proportional fonts and multiple sizes much better
Everyone has... I'm not sure it's a big loss, but I found it funny to "fix" and the fun of tweaking tiny things like that lies at the core of a whole lot of terminal bikeshedding...
> i've been thinking that maybe nested tables would be better than character cells, for example, accommodating proportional fonts and multiple sizes much better
A lot of simplicity could easily go away if it's not done well, but I like the idea. I want to eventually support some limited "upgrades" in that direction, but will take some cleanup efforts before that'll be priority.
I will want to see support for the PC character set, for EUC character sets (including EUC-TRON), TRON-8 character code, bitmap fonts (including non-Unicode fonts), Xaw-like scrolling and xterm-like selecting, ability to disable receiving (not only sending) 8-bit controls (which should be used to switch between EUC-JP and EUC-TRON, as well as other purposes), a "universal escape" sequence (recognized anywhere even in the middle of other sequences), and some security features (I have some ideas that I don't even know if it is possible on Linux or on BSD, such as checking the foreground process, and being able to discard any data the terminal emulator sent to the application program that as not yet been read yet, which can avoid a file or remote server to send answerbacks which will execute commands in the shell, if you add a cancellation code into the shell prompt, etc)
It made sense as a service tool on a physical terminal, not much now. It's not that it's a problem - it's trivial to support. It's just that it is an example of one of hundreds of little features that are box-ticking exercises that would cause about a dozen people worldwide to shrug if they noticed they weren't there before they'd do the same thing a slightly different way and not think about it again.
Some of the ones you list are useful some places, but many of them don't need to be in every terminal. I want more, but smaller, options built from generic reusable components.
E.g. most of my terminal does not care what your character set is, or what type of fonts you want to use, or how your scrolling works, or if you have scroll bars, or whether there is a shell, or whether there's a program being run by a terminal vs. a program embedding the terminal, or if it's running in a window, or whether it has GUI output at all or is entirely headless. This is true for most terminals. Yet these components are rarely separated and turned into reusable pieces of code.
Most of the features you list are features I don't need, won't implement, and don't care about. But what I do care about is that with some exceptions (e.g. the Gnome VTE widget) most terminals reinvent way too much from scratch (and frankly most users of terminal widgets like VTE still reinvent way too much other stuff from scratch), put too much effort into supporting far too many features that are rarely used, instead of being able to pick a terminal that is mostly just pulling in generic components as a starting point.
The result is a massive amount of code that represents the same features written over and over and over again and sucking time out of the bits that differentiate terminals in ways useful to users.
E.g. just now I've been starting to untangle the bits in my terminal that handles setting up the PTY and marshaling IO between the shell or other program running in it and the bits that handle the output to the actual terminal. The goal is to make it as easy for casual scripts to open a terminal window and control it as it was on the Amiga, without having to spawn a separate script to "run in it".
On the Amiga you could e.g. do "somecommand >CON:x/y/w/h/Sometitle" to redirect "somecommands" output to a separate terminal window without any foreground process, with the given dimensions and title, and assorted other flags available (e.g. "/CLOSE" would give yo ua close button, "/WAIT" would keep the window open after the process that opened it went away etc.).
If you've written a terminal, then part of your terminal represents 99% of the code to provide something almost like that.
Beyond that, I'm going to rip the escape code handling out too, so code that don't want to do escape codes still can pretend they're talking to a terminal with a somewhat ncurses-y interface but with the freedom to redirect the rendering or render on top of it, or whatever, then that makes "upgrading" from a terminal UI to somewhat of a GUI far easier (the Amiga, again, had a lot of this, with apps that'd mix the same system console handler used for the terminal with few graphical flourishes; it lowered the threshold to start building more complex UI's immensely)
Then I'm going to extract out the actual rendering to the window into a separate component from the code that maintains the (text) screen buffer, so that I can write code that uses the same interface to render either to a terminal or directly to a window.
Same for e.g. font-handling - I've decided I don't care about bitmap fonts, but the actual bit of my terminal that cares about any kind of fonts is ~40 lines of code, and to most of my terminal components it doesn't matter if you output anything anywhere, but even of the remaining code actually dealing with GUI output, 3/4 doesn't care, or know, about fonts at all (managing a window, clearing, filling, scrolling take up more). Making it pluggable so someone could plug in either a client side bitmap font renderer or code to use the old X11 text drawing calls is trivial.
Because with all of these things broken out into components it doesn't matter much if my terminal doesn't support your feature set, if "writing another terminal" doesn't mean writing the 95% of the code that implements shared functionality over and over again.
You could write literally half a dozen of custom tiny terminals like that before even approaching the line count of xterm's mouse button handling code alone.
No. There is another.
https://en.m.wikipedia.org/wiki/Arthur_Whitney_%28computer_s...
I appreciate brevity, but I feel there's a fundamental disconnect between people who want to carefully read code symbol by symbol, who often seem to love languages like J or K, or at least he better able to fully appreciate them, and people like me who want to skim code and look at the shape of it (literally; I remember code best by its layout and often navigate code by appearance without reading it at all, and so dense dumps of symbols are a nightmare to me)
I sometimes think it reflects a difference between people who prefer maths vs languages. I'm not suggesting one is better than the other, but I do believe the former is a smaller group than the latter.
For my part I want grammar that makes for a light, casual read, not having to decipher. I want to be able to get a rough understanding with a glance, and gradually fill in details, not read things start to finish (yes, I'm impatient)
A favourite example of mije is the infamous J interpreter fragment, where I'd frankly be inclined to prefer a disassembly over the source code. But I also find the ability to sketch out such compact code amazing.
I think Wirths designs very much fit in the languages that are skimmable and recognisable by shape and structure category. I can remember parts of several of Wirths students PhD theses from the 1990s by the shape of procedures in their Oberon code to this day.
That's not to diminish Whitney's work, and I find that disconnect in how we process code endlessly fascinating, and regularly look at languages in that family because there is absolutely a lot to learn from them, but they fit very different personalities and learning styles.
textb = 'What hath the Flying Spaghetti Monster wrought?'
bits = (right_shift.outer(array([ord(c) for c in textb]),
arange(8))).ravel() & 1
and i wrote them myself three months ago, with reasonably descriptive variable names, in a language i know well, with a library i've been using in some form for over 20 years, and their output was displayed immediately below, in https://nbviewer.org/url/canonical.org/~kragen/sw/dev3/rando...i had every advantage you could conceivably have! but i still guessed wrong at first and had to correct myself after several seconds of examination
i suspect that in j or k this would be something like (,(@textb)*.$i.8)&1 though i don't know the actual symbols. perhaps that additional brevity would have helped. but i suspect that, if anything, it would have made it worse
by contrast, i suspect that i would have not had the same trouble with this
bits = [(ord(c) >> i) & 1 for c in textb for i in range(8)]
however, as with rpn, i suspect that j or k syntax is superior for typing when what you're immediately evaluating expressions rather than writing a program to maintain later, because the amount of finger typing is so much less. but maybe i just have a hard time with point-free style? or maybe, like you say, it's different types of people. or maybe i just haven't spent nearly enough time writing array code during those years textb = 'What hath the Flying Spaghetti Monster wrought?'
p textb.bytes.product((0...8).to_a).map{_1>>_2}.map{_1 & 1}
Or with some abominable monkey patching: class Array
def outer(r) = product(r.to_a)
def right_shift = map{_1>>_2}
end
p textb.bytes.outer(0...8).right_shift.map{_1 & 1}
I think this latter is likely to be a closer match to what you'd expect in an array language in terms of being able to read in a single direction and having a richer set of operations. We could take it one step further and break the built in Array#&: class Array
def &(r) = map{_1 & r}
end
p textb.bytes.outer(0...8).right_shift & 1
Which is to say that I don't think the operator-style line-noise nature of K is what gives it its power. Rather that it has a standard library that is fashioned around this specific set of array operations. With Ruby at least, I think you can bend it towards the same Array nature-ish. E.g. a step up from the above that at least contains the operator overloading and instead coerces into a custom class: textb = 'What hath the Flying Spaghetti Monster wrought?'
class Object
def k = KArray[*self.to_a]
end
class String
def k = bytes.k
end
class KArray < Array
def outer(r) = product(r.to_a).k
def right_shift = map{_1>>_2}.k
def &(r) = map{_1 & r}.k
end
p textb.k.outer(0...8).right_shift & 1
With some care, I think you could probably replicate a fair amount of K's "verbs" and "adverbs" (I so hate their naming) in a way that'd still be very concise but not line-noise concise. p textb.bytes.map{|b| (0...8).map{|i| (b>>i) & 1} }.flatten
EDIT: This is kind of embarrassing, but we can of course do just this: textb.bytes.flat_map{_1.digits(2)}
But I think the general discussion still applies, and it's quite interesting how many twists and turns it took to arrive at thatk doesn't have a right shift operator, but you don't need that, you can use the base encoding operator instead
Personally I think this is clearer than both the array-ish python and the list comp.
https://ngn.codeberg.page/k/#eJwrSa0oSbJSCs9ILFEA4gyFkoxUBbe...
oh, apparently a new one called ngn/k: https://codeberg.org/ngn/k
I'm wildly guessing that your approach somehow ends doing something closer to this:
textb.bytes.flat_map{_1.digits(2)}
Which I must admit it took me embarrassingly long to think of.https://gist.github.com/vidarh/3cd1e200458758f3d58c88add0581...
The big caveat being it clicked too late that 1) "encode" is not "change base and format", but "for each element in this array, apply the module to the entire other array, and pass the quotient forward", and 2) encode returns a list of columns of the remainders rather than rows (the output format really does not make this clear...).
So you can turn a list of seconds into hour, minute, seconds with e.g.: (24 60 60)\(86399 0 60), but what you get out is [hour, minute, second] where hour, minute, second each are arrays.
If you want them in the kind of format that doesn't break the minds of non-array-thinking people like us because the order actually matches the input, you'd then transpose them by prepending "+", because why not overload unary plus to change the structure of the data?
+(24 60 60)\(86399 0 60)
Returns
(23 59 59
0 0 0
0 1 0)Or [[23,59,59], [0,0,0], [0,1,0]] in a saner output format that makes it clear to casuals like me which structure is contained in what.
Now, if you then want to also flatten them, you prepend ",/"
I feel much better now. Until the next time I spend hours figuring out a single line of k.
The relevant line is
I\ encode 24 60 60\3723 -> 1 2 3 2\13 -> 1 1 0 1
So (8#2)\x encodes x in binary, with 8 positions. And because k is an array language, you don't need to do any mapping, it's automatic.
,/+|x is concat(transpose(reverse(x))) (,/ literally being 'reduce with concat')
Almost as impenetrable as the code unless you already know the language. But that's ok - I guess that's the main audience...
E.g. trying to figure out what "\" means, in that help is only easier now because you gave me the whole line, as there are 64 occurrences of "\" in that doc and I wouldn't have known what pattern to search for to limit it...
It's back to the philosophical disconnect of expecting people to read start to finish/know far more detail inside out rather than skimming and relying on easy keyword lookups... (yes, we're lazy)
> 'reduce with concat'
So "flatten" in Ruby-speak, I take it (though "flatten" without an argument in Ruby will do this recursively, so I guess probably flatten(1) would be a more direct match).
> you don't need to do any mapping, it's automatic.
After these pointers (thanks!), here's - mostly for my own learning, what I ended up with not in an attempt to get closer to the linenoise (we can do that with a horrific level of operator overloading that'd break most of the standard library, though we can't match k precisely). Please don't feel obliged to go through this unless you're morbidly curious; I just had to, but I'm sure you'd suffer going through my attempt at figuring out what the hell k is doing...:
textb="What hath the Flying Spaghetti Monster wrought?"
# Firstly, I finally realised after a bunch of testing that 1) "(8#2)" does something like this.
# That is, the result of 8#2 is (2 2 2 2 2 2 2 2), which was totally not what I expected.
def reshape(len,items) = [].fill(0...x)
class Integer
# For the special case of (x#y) where x is a positive integer, which is frankly the only one
# I've looked at, we can do:
# So now 4.reshape(2) returns [2 2 2 2] just like (4#2) in ngn/k
def reshape(items) = Array(items)*self
# Now we can do something somewhat like what I think "encode" is
# actually doing - this can be golfed down, but anyway:
# With this, "a".ord.encode(8.reshape(2)) returns [0,1,1,0,0,0,0,1],
# equivalent to (8#2)\ "a" in ngn\k
def encode(shape)
rem = self
Array(shape).reverse.map do |v|
val = rem % v
rem = rem / v
val
end.reverse
end
end
# Now we can break Array too.
class Array
# First a minor concession to how Ruby methods even on the Array
# class sees the focal point as the Array rather than the elements.
# E.g. `self` in #map is the Array. If the focus is to be on applying the
# same operation to each element, then it might be more convenient
# if `self` was the element. With this, we can do ary.amap{reverse}
# instead of ary.map{|e| e.reverse} or ary.map{ _1.reverse}.
# To get closer to k, we'd have needed a postfix operator that we could
# override to take a block, but unfortunately there are no overridable
# postfix operators in Ruby. E.g. we can hackily make
# ary.>>(some_dummy_value) {a block} work, but not even
# ary >> (some_dummy_value) { a block} and certainly not
# ary >> { a block }
#
def amap(&block) = map { _1.instance_eval(&block) }
# If we could do a "nice" operator based map, we'd just have left it
# at that. But to smooth over the lack of one, we can forward some
# methods to amap:
def encode(...) = amap{encode(...)}
# ... with the caveat that I realised afterwards that this is almost certainly
# horribly wrong, in that I think the k "encode" applies each step of the
# above to each element of the array and returns a list of *columns*.
# I haven't tried to replicate that, as it breaks my mind to think about
# operating on it that way. That is, [65,70].encode(2.reshape(10))
# really ought to return [[6,7],[5,0]] to match the k, but it returns
# [[6,5],[7,0]]. Maybe the k result will make more sense to me if I
# take a look at how encode is implemented...
def mreverse = amap{reverse}
end
# Now we can finally get back to the original, with the caveat that due to
# the encode() difference, the "mreverse.flatten(1)" step is in actuality
# working quite differently, in that for starters it's not transposing the arrays.
#
p textb.bytes.encode(8.reshape(2)).mreverse.flatten(1)
# So to sum up:
#
# textb -> textb.bytes since strings and byte arrays are distinct in Ruby
# (8#2) -> 8.reshape(2)
# x\y -> y.encode(x) ... but transposed.
# |x -> x.mreverse
# ,/+x -> x.flatten(1) .. but really should be x.transpose.flatten(1)
#
# Of course with a hell of a lot of type combinations and other cases the k
# verbs supports that I haven't tried to copy.This dichotomy exists in mathematics as well. Some mathematicians prefer to flood the page with symbols. Others prefer to use English words as much as possible and sprinkle equations here and there (on their own line) between paragraphs of text.
The worst are those that love symbols and paragraphs, writing these dense walls of symbols and text intermixed. I’ve had a few professors who write like that and it’s such a chore to parse through.
perhaps more significant than tracemonkey was luajit, which achieves much higher performance with the tracing technique
Just thought about that when Donald Knuth's Christmas lecture https://www.youtube.com/live/622iPkJfYrI lead me to one of his first TeX lectures https://youtu.be/jbrMBOF61e0 : If I install TeX on my Linux machine now, is that still compiled from the original Pascal source? Is there even a maintained Pascal compiler anymore? Well, GCC (as in GNU compiler collection) probably has a frontend, but that still does not answer the question about maintenance.
These were just thoughts. Of course researching the answers would not be overly complicated.
Of course
Of course, you can always build the latest version from Git.
If you install TeX via the usual ways–TeX Live and MikTeX are the most common—then the build step runs a program (like web2c) to convert the Pascal source (with changes) to C, then uses a C compiler. (So the Pascal source is still used, but the Pascal "compiler" is a specialized Pascal-to-C translator.) But there is also TeX-FPC (https://ctan.org/pkg/tex-fpc), a small set of change (patch) files to make TeX compilable with the Free Pascal compiler (https://gitlab.com/freepascal.org/fpc/).
For more details see https://tex.stackexchange.com/questions/111332/how-to-compil...
Absolutely!
And equally important was his ability to convey/teach CS precisely, concisely and directly in his books/papers. None of them have any fluff nor unnecessary obfuscation in them. These are the models to follow and the ideals to aspire to.
As an example see his book Systematic Programming: An Introduction.
Finally a short story for the record. In 1968, the Communications of the ACM published a text of mine under the title "The goto statement considered harmful", which in later years would be most frequently referenced, regrettably, however, often by authors who had seen no more of it than its title, which became a cornerstone of my fame by becoming a template: we would see all sorts of articles under the title "X considered harmful" for almost any X, including one titled "Dijkstra considered harmful". But what had happened? I had submitted a paper under the title "A case against the goto statement", which, in order to speed up its publication, the editor had changed into a "letter to the Editor", and in the process he had given it a new title of his own invention! The editor was Niklaus Wirth.
[1] Transcription - https://www.cs.utexas.edu/%7EEWD/transcriptions/EWD13xx/EWD1... PDF - https://www.cs.utexas.edu/%7EEWD/ewd13xx/EWD1308.PDF
After playing around a bit with Basic on the C64/128, Pascal became my first "real" programming language I learned. In the form of UCSD Pascal on Apple II at my school as well as Turbo Pascal 3.0 on a IBM PC (no AT or any fanciness yet). Actually a Portable PC with a build-in amber CRT.
When I got my Amiga 500, Modula 2 was a very popular language on the Amiga and actually the M2Amiga system was the most robust dev env. I still think fondly of that time, as Modula 2 made it so easy to develop structured and robust programs. The module concept was quite ahead of the time, while the C world kept recompiling header files for so many years to come. Today, Go picked up a lot from Modula 2, one reason I immediately jumped onto it. Not by chance, Robert Griesemer was a student of Wirth.
During the 90ies, while MS Dos was still used, Turbo Pascal still was the main go-to language on the PC for everyone, as it was powerful, yet approachable for non-fulltime software developers. It picked up a lot of extensions from Modula 2 too and also had a nice Object system. It peaked at the version 6 and 7. Probably to the day my favorite development environment, partially because of the unmatched speed of a pure character based UI. And Turbo Pascal combined the nice development environment with a language which found a great compromise between power and simplicity.
Unfortunately, I was only vaguely familiar with his later work on Oberon. I ran the Oberon system natively on my 386 for some toying around. It was extremely impressive with its efficiency and full GUI in the time of DOS on the PC. A pity, it didn't achive more attention. Probably it would have been very successful if it had gained tracking in the not too late 80ies, in the early 90ies of course Windows came along.
From a puristic point of view, the crowning achievement was of course when he really earned the job title of a "full stack developer", not only designing Oberon and the OS, but the CPU to run it as well. Very impressive and of a huge educational value.
END.
[1]: https://en.m.wikipedia.org/wiki/Delphi_(software)
[2]: https://en.m.wikipedia.org/wiki/List_of_low-code_development...
> Low-code development platforms trace their roots back to fourth-generation programming language and the rapid application development tools of the 1990s and early 2000s.
> Delphi was originally developed by Borland as a rapid application development tool for Windows as the successor of Turbo Pascal.
Comments: <https://news.ycombinator.com/item?id=17132058>
Better C/C++ compilers and libraries can help, but the original C language and standard library were certainly part of the issue. Java and JavaScript (etc.) may have their issues but at least chasing down pointer errors usually isn't one of them.
Unfortunately Pascal only mattered to legions of Mac and PC developers.
I considered telling him that he could get most of the things (he also buys various components) for free today, but then.. he is about 5 years before retirement and won't relearn all his craft now.
Myself, I am not sure whether its nostalgia but I miss the experience of Delphi 7 I started with 20 years back. In many ways, the simplicity of VLC and the interface is still unbeaten.
So about three months my senior.
> I considered telling him that he could get most of the things (he also buys various components) for free today, but then.. he is about 5 years before retirement and won't relearn all his craft now.
Free Pascal / Lazarus shouldn't be all that much to relearn.
> Myself, I am not sure whether its nostalgia but I miss the experience of Delphi 7 I started with 20 years back.
Delphi 1, 28 years now.
> In many ways, the simplicity of VLC and the interface is still unbeaten.
1) Yup.
2) VCL, btw.
3) Now that Embarcadero is hiking up the price of Delphi with every release, I think the standard-bearer for best librry / framework is probably the LCL, the Lazarus Component Library.
I learned C++ later myself as a Pascal with bizzare syntax. I always felt like semantics of C++ was taken entirely from Pascal. No two lanuages ever felt closer to each other for me. Like one was just reskin of the other.
I adopted C++ right away as the sensible path beyond Turbo Pascal for cross-platform code, and never seen a use for C primitive and insecure code, beyond being asked to use it in specific university projects, and some jobs during the dotcom wave.
On Usenet C vs C++ flamewars, there might be still some replies from me on the C++ side.
We already had autoformatting tools in the early 1990's, Go did not invent them.
I like Wirth's whole software stack: RISC-5 (not to be confused with RISC-V) implemented in Lola, Oberon the language, and Oberon the environment. IIRC Lola can generate Verilog - I think the idea was that students could start with an FPGA board and create their own CPU, compiler, and OS.
I also like his various quips - I think he said something like "I am a professor who is a programmer, and a programmer who is a professor." We need more programmer/professors like that. Definitely an inspiration for systems people everywhere.
One of his quotes: "Whereas Europeans generally pronounce my name the right way ('Ni-klows Wirt'), Americans invariably mangle it into 'Nick-les Worth'. This is to say that Europeans call me by name, but Americans call me by value."
(* might Carver Mead describe him as a metaphorical "tall, thin, person"?)
I'm much more sad when life sort of decays (Alzheimer's, dementia, or simply becoming slow/stupid/decrepit), ends early, or when life is simply wasted.
He was about to turn 90.
He lead a long, impactful, fulfilling life.
That's a life to celebrate.
Heaven is happier by one person now for sure, again. And maybe some compilers over there also need tinkering. Rest in peace, Mr Wirth.
He gave a talk at the CHM (He was inducted as a fellow in 2004) I got to talk with him and was really struck by someone who had had such a huge impact was so approachable. When another person in the group challenged Modula-2 he listened respectfully and engaged based on the the idea that the speakers premise was true, then nicely dissented based on objective observations. I hope I can always be that respectful when challenged.
Learned most of it from a wonderful book whose name I have forgotten, it had a wrench on its cover, I think?
Anyway, still rocking Pascal to this day, since I still maintain 3 moderately complex installers written with InnoSetup, which uses RemObjects Pascal as a scripting language.
4 years ago, a new guy on our team, fresh from school, who never even knew this language existed, picked up Pascal in a week, and started maintaining and developing our installers much further. He did grumble a bit about the syntax but otherwise did a splendid job. I thought that was a tribute to the simplicity of the language.
Me too, word for word. I spent a few years in my pre-teens immersed in the Turbo Pascal IDE, which was a full-on educational tool of its own that explained everything about the language. I moved on to C after that, but I still get a nostalgic vibe from looking at Pascal syntax. It was a great foundational experience for me as a programmer.
I still to this day dont understand why vast majority of people in programming dislike the syntax of Pascal.
Also, his Oberon system provides a rich seam to mine. This, from a symposium held at ETH Zurich on the occasion of his 80th birthday in 2014, is a whirlwind retrospective. "Reviving a computer system of 25 years ago" https://www.youtube.com/watch?v=EXY78gPMvl0
One of the greats.
What an innovator and a role model. I wish I can be as passionate about my work in my 80's as him.
Ranged numerics and enumerations were part of that.
In the end, it was definitely worth the effort, and I learnt good habits from it. I used it in college, and I suppose I kinda still do, because I do a lot of PL/SQL.
He was hugely important for generations of coders.
RIP.
his preference for clarity over notational fancyness inspired so many of us.
the Pascal family of languages are not only syntactically unambiguous to the compiler they are also clear and unambiguous to humans. can. the Carbon successor to c++ strives for the same iirc.
I couldn't find it available for download anywhere, but Internet Archive has a version of it in the library that can be checked out for free: https://archive.org/details/modelimplementat0000wels/page/n9...
compiler.pas can be compiled with a modern Pascal compiler, but the resulting compiler cannot compile itself. I don't know if that's caused by a transcription error, a bug in the modern compiler or a bug in the Model Implementation.
I would love it if somebody gets this working. I don't think I myself will continue with this project.
https://people.inf.ethz.ch/wirth/CompilerConstruction/Compil...
"Algorithms + Data Structures = Programs" was a seminal book for me when I was learning about software development and it has influenced how I think about programming. Also, Pascal (in its various dialects) was my main language for many years on multiple platforms (CP/M, MS-DOS, Windows).
I'm also thankful for references to "timeless" Pascal books or online teaching materials that would be accessible for a 10+ year old kid who is fine with reading longer texts.
(My condolences are below, fwiw. His death is, interestingly, a moment of introspection for me, even if I'm just a hobbyist interested in small systems and lean languages.)
I would rather pick Python or Kotlin.
I think it's still the best language to start with.
And don't let yourself be dissuaded by comments here about "no ecosystem" etc; that's BS, IMnsHO. There are tons of compilers and IDEs you could use, from Free Pascal (with or without Lazarus), via PowerPascal (IIRC) and other smaller implementations, to the old versions that Borland / Inprise / CodeGear / Idera / Embarcadero have released as freeware over the years.
Fond memories; I feel like the 90s kids were the last ones to really get to appreciate Pascal in a "native" (supportive, institutional) setting.
I also loved learning Oberon/Bluebottle (now A2 I guess), which I was so fascinated with. I think that and Plan 9's textual/object interface environments are super interesting and a path we could have taken (may converge to someday?)
I learned pascal fairly late in the grand scheme of things (basic->6502 assembly->C and then eventually pascal) but it was used for the vast majority of my formal CS education first by instruction, then by choice, and eventually in my first real programming job. The later pascal dialects remain IMHO far better than any other languages I write/maintain/guide others in using. Like many others of his stature it was just one of his many hits. Niklaus Wirth is one of the giants I feel the industry stands on, and I thank him for that.
"All those moments will be lost in time, like tears in rain..."
https://news.ycombinator.com/item?id=27661559
RIP
A double pointer was just two carets. And so on.
There was a struct symmetry about the whole thing. C broke that, especially with strict pointers.
I think tonight is the time to start on 4A, before we lose Knuth too.
And as I picked it down I noticed that, almost by coincidence, AoCP stood next to Wirth's PiM2. It wasn't intentional but it feels very right. There's a set of language books that end with Systems Programming with Modula 3, the Lua book. Thinking Forth, PiM2, then a gap, then the theory-ish section starts with five volumes of Knuth. Sigh.
RIP Niklaus Wirth.
I probably wouldn't have learned algorithms and data structures as[S] well without Pascal but I never learned it right until C eventually cameawrong.
PS: We still have Dr. Donald Knuth with us :)
Some people here are recommending greats books he wrote, definitely worth reading.
Pascal was the first programming language I ever learned, and a book on it that he coauthored was the first programming book I ever purchased. I hold him (and Pascal) in a special place in my heart.
But I can't find a reliable attribution.
[edit - see other comment, apparently said by Adriaan van Wijngaarden not Wirth]
If I had read more closely to the wording, the language was _designed_ by Wirth but that doesn't necessiate him being fingers-to-keyboard (or whatever modality) despite it saying he was the developer.
For every, there is an
end.RIP
Didn't someone already publish a draft of his last book? I think I read that somewhere...
I need to read his compiler book once I completed my toy interpreter.
Bwahah! Bwahah!
This is a very sad day.
Anyway: I trust we're just seeing natural human latency here, but this clearly merits the HN black bar. RIP, Nik Wirth -- truly one of the giants, and someone whose work had a tremendous personal influence for so many of us!
For me something like a black banner signifies a tragedy, not merely a death. A bunch of children being shot, a war, a disease ravaging a country, etc.
I’m curious to learn others’ perspectives however.
and a black ribon signifies a great loss, not a tragedy.
So as it apparently does need to be said: we're humans -- we mourn our dead. That is, the black bar denotes death, not tragedy; when we mourn those like Wirth who lived a full life, we can at once take solace in the fullness of a life lived and mourn that life is finite. The death rite allows us to reflect on the finiteness of our own lives, and the impact that Wirth had us, and the impact that we have on others. You are presumably too young to have felt this personal impact, but I assure you that many are brought back to their own earliest exposure to computing -- for many of us, was Pascal.
Again, RIP Nik Wirth; thank you for giving so many of us so much.
Niklaus Wirth contributed quite a bit to our field, and, directly or indirectly, impacted many of the people who frequent this (programming technology oriented) site.
It can be "kind of a best case scenario" and yet you still mourn the loss. Mourning doesn't require a tragedy.
My grandmother died in her sleep at 94, pretty healthy all things considered (still had a good head, could putter along, and was in her own home of more than 60 years), after having had a great day. Pretty much the best death she and we could have hoped for. I still wouldn't have minded having her nearby for a few more years.
Personally, I don't agree, to me it's just not as sad or shocking. People don't live forever and Wirth's life was as successful and complete as possible. It's not a "black day" where society truly lost someone before they fulfilled their potential.