Oberon-2, a hi-performance alternative to C++ (1997)
folk.ntnu.no
folk.ntnu.no
Wirth's graduate student Michael Franz took this a bit further and adapted to storing Java programs as compressed SSA control flow graphs, called SafeTSA. Franz's research group modified the Jalapeno (now JikesRVM) JVM to be able to load SafeTSA files as well as regular Java class files. They found that the time to JIT code was reduced, and the performance of the generated native code was faster when using SafeTSA. The downside is that SafeTSA isn't well suited to interpretation in a HotSpot style hybrid JIT. So, program start-up time would likely suffer unless using AoT compilation. Last time I played around with A2/Bluebottle/AOS, I believe compressed syntax trees were the on-disk program format.
Beyond that, I just wanted to add a couple of things:
It doesn't require keeping the generated datastream at the level of the AST per see. You can generate as high- or low-level data as suitable where e.g. a lower level representation requires additional computation but remains independent of hardware architectures. The key is just that what you generate should fit a tree structure. So you can do any number of optimisation steps on the tree prior to applying the semantic dictionary encoding step if you choose. The extension to e.g. serialising SSA forms was pretty natural - there was work at ETHZ on generating SSA form around the same time as well [2]
The real beauty of the method is that the encoder and decoder each generates a dictionary the same way e.g. LZW does (and the dissertation cites Welch; Lempel-Ziv style variable length dictionary entries is achieved by simply variable length encoding all integers stored), but where the encoder uses it to compress and flatten the tree, the decoder does not reconstitute the original tree, but uses it to generate machine code fragments directly, and either copy it straight into the code being generated or stores it as a template to reuse for generation of other occurrences of that dictionary element.
The compression/decompression and serialization/code-generation are one and the same, and avoiding a separate layer is a key reason why it achieved the speed it did.
[1] Code-Generation On-the-Fly: A Key to Portable Software, M. Franz (1994): https://oberoncore.ru/_media/library/franz_m.code-generation...
[2] Single-pass generation of static single-assignment form for structured languages, M. Brandis, H. Mössenböck (1994)
It is done as part of the compiler settings when compiling a module, if it ends up in pure binary or abstract syntax trees format. Some flags depend on the compilation mode.
As an aside, Oberon-2 isn't a C++ alternative because it apparently has mandatory garbage collection, which isn't acceptable to C++ programmers now (which is why they're going to Rust instead of Go) and damned well wasn't acceptable to them in 1997, else they would have moved to Java.
c++ projects today are very different from c++ projects in the 01990s when basically all 'serious' software was either c++ or c
a lot of c++ projects in the 01990s were what we think of now as java projects or c# projects (or django projects): line-of-business systems full of boilerplate and shitty architecture, not streamlined mega-optimized aaa game engines or web browsers
c++ projects today are the niche that was left over, which is, yeah, basically the cases where you can't afford gc
— ⁂ —
the appeal of wirth languages is that you get 90% of the value of a monstrosity like pl/i or c++ for about 5% of the complexity
language complexity hurts you three times: when you are learning the language, when you are choosing a compiler, and when you are debugging your code
so actually sometimes you get 200% of the value of a monstrosity like pl/i or c++
hopefully at some point someone will develop a wirthified version of rust, but i don't think wirth will do it this time
You got the year wrong with that '0' prefix. The decade you're referring to is the 11990s.
https://longnow.org/ideas/long-now-years-five-digit-dates-an...
Good luck getting people to agree on anything.
So this is now the year 2,002,023 FE (Fire Era).
As an example, as late as '99, my first large web application was C++, and that was not an unusual choice at the time.
Most of the stuff I have exposed to the Internet written in C or C++, has done so by using a safer managed language and only accessing the native code via libraries.
Naturally there is the issue of on which language those runtimes were written on, but even so, the trusted computing base is much smaller than having the application fully written in C++, with possible C idioms while parsing data coming out of the network.
Many did move to Java (or .NET), that is one of the reasons C++ is hardly seen in distributed computing stacks 20 years later, or most CNCF projects, other than existing OSes and RDMS.
Those targeting OSes like Android, or ChromeOS, hardly have an option, unless they feel like using their own OpenGL/Vulkan based GUI, and create JNI wrappers (or JS callbacks) for like 80% of the OS surface .
A Wirth-style compiler for a Wirth-style language can be written fairly rapidly by a single person, and the languages, compilers and runtime systems can be easily understood. The Oberon-07 language report, including the grammar, is 17 pages.
If simplicity doesn't matter to you, then this won't appeal to you, and that's fine. I mostly use Ruby these days, whose parser (in MRI anyway) is likely longer than the whole Oberon-07 language report alone (I haven't measured, but ca. Ruby 1.8.6 the parser alone was 6k lines+), so I've surrendered the simplicity, but it still stands out to me as an ideal and inspiration.
Wirth was perhaps too ascetic in his insistence on keeping the compilers minimal, but you can fine less extreme versions carried on in extensions done in the PhD dissertations of students in his group.
Maybe too ascetic in terms of getting the most adopters, but a wonderful reminder to designers of development tools that most designs are probably far more complex than they need to be, even when optimizing for non-simplicity criteria.
So not a wasted effort!
Well, simplicity in a programming language is IMO only a virtue as long as it doesn't come at the cost of too much functionality, which with Pascal it surely did.
There's a reason that of C and Pascal, C is the one that survived. It's the simplicity of Pascal that ultimately killed it, since it forced any practical implementation to heavily extend the language (Turbo Pascal, Apple's Clascal, etc). Even USCD's famous P-System extended as much as it subsetted the language. What this meant is there was in practice no Pascal standard, only these isolated ecosystems which eventually died one at a time.
In contrast to Pascal, C (which was designed only a couple of years later) was functionally complete enough, even in original K&R form, that it was implemented as a standard, and the standard endured past the lifetime of any individual implementation.
The simplicity of Pascal also came at extreme cost in terms of performance and usability. For example most non-trivial Pascal programs (certainly any use for systems programming) end up using the "variant record hack" for type casts (e.g. pointer to int). So, rather than not allow type casts, Pascal allowed it but made it have a runtime cost of load/store rather than being free. Or another example - Pascal only supported value or var (passed by reference) function parameters, so if you want to efficiently pass a record or array type you'd have to pass it as "var" to avoid being copied, even if the caller wasn't meant to modify it.
Hmm.. or how about Pascal's "simple" for loops, which took the form "for i := x to y", which means you couldn't use it for "i = 0 to (N-1)" without the cost of calculating "N-1" and would have to use a while or repeat loop for that instead at the cost of readability/maintainability.
Or how about Pascal's (variable sized) "conformant array" function parameters which the language didn't allow you to pass to another function... Or how about the fact that Pascal didn't support length-1 strings (length 0 and 2+ ok, though), since it made things "simple" by regarding 'a' as a char vs 'ab' as a string (incompatible types).
Yes, it was simple, but very poorly thought out, and practically useless unless extended.
Focusing on Pascal is also irrelevant to the point I made, which was to answer the general point of the appeal of Wirth's languages.
I get the appeal of a simple language, but was just pointing out that in addition to compromising functionality it also lead to the death of Wirth's languages by means of there effectively being no standard.
I've probably had as much exposure to Wirth's languages as anyone:
- Learnt Algol-W in college c. 1979 - Implemented ISO-Pascal (me +1) at Acorn (UK) c.1982 - Used Modula-2 to program parts of PANOS at Acorn
Have spent the last few months programming in Acorn ISO Pascal on an 8-bit emulator (BeebEm), struck by how awful the language itself really was!
That said, "death" is too harsh, all the time there are multiple implementations of both Wirth-derived (the most prominent being Delphi, which is still being developed and sold) and "pure" Wirth languages available and in use. If anything, I find it more remarkable that they've been seen as much adoption as they have outside of teaching. There's even a very expensive commercial ARM implementation of Oberon-07.
It's in any case largely irrelevant to the question of their appeal.
I don't want to work in Oberon (or Pascal) any more than you do (but, to tie it back to this article, I also don't want to work in C++ unless I have to), but in terms of distilling lessons of what is needed from a language, and a learning tool, it is up there with Smalltalk and Lisp (other languages I don't want to work in) as one of a small handful of the language designs that will continue to influence for a long time to come.
And that is a large part of their appeal - not for day to day use, but for the lessons you can learn from them. I don't care if nobody uses them. I care about the research they contributed to and spawned, and what I can still learn from them.
> and removed stuff like enumeration types and variant records.
Oberon replaced variant records with extended types, type tests, and procedure variable (function pointers), sufficient to do OOP. In terms of his goal of focusing on sticking to the bare essentials, it was a good tradeoff, and it sparked novel work.
He certainly left his mark on the computer industry in terms of products based on his work, but would, say, a hypothetical "Turbo Algol" have been much different than "Turbo Pascal"? The popularity of Turbo Pascal (& Delphi) really had more to do with the implementation (speed, IDE, language extensions) than the base language itself.
C# is influenced by Pascal and Modula-2 because Anders Hejlsberg had a huge hand in creating it.
Go and Lua are also quite heavily influenced by the Wirthian languages.
Ada is also a direct descendant of Pascal and Modula-2. For a while in the 80's, Ada was more popular than C++ because of the DoD standardizing on it but the DoD and NASA still use it today. NASA's Artemis project uses Ada. The Canadian air traffic controls system all run Ada.
Wirthian languages have been focused on safety for 50 years. The industry is just now catching up with safety-focused Rust as a first step. It's long overdue. C and C++ were dangerous enough offline but using them online is just asking to get hacked. Sure, safe code can be written in them but not many C and C++ programmers are capable of doing so. Why wouldn't you want a language that handled the bulk of that for you?
Sure Ada saw some success, and is even a pretty decent language, but I fail to see that there is much Wirth in there, certainly not his minimalist aesthetic!
So there is a little bit of Wirth on ALGOL as well.
There's a useful language family tree on the WikiPedia ALGOL page. ALGOL is really the grandfather of all modern structured programming languages. I guess the way it was named "Algol = Algorithmic language" about says it all.
You've been given the answer already: Simplicity. You keep ignoring it.
Both of implementation (which can be met by e.g. Smalltalk, Lisp, Scheme, Forth as well), and of usability (which some might argue the former could, but the reality is most people have gravitated towards languages closer to the ALGOL family for usability).
And yes, it's "tweaks" on ALGOL, but those tweaks mattered, in terms of simplifying implementation. ALGOL users expected gc (though it was not technicall required), Pascal users didn't (though it wasn't forbidden). ALGOL's added complexity around type checking of variants mattered. ALGOL's file IO was bizarre (giving me horrible flashbacks to Simula's equally broken IO), and ALGOL 68's definition of formatted IO took up several dozen pages of the spec... ALGOL supported user defined precedence rules for operators, which, while it's not hard to implement is significant added complicity vs. a typical hardcoded recursive descent parser for Pascal, Modula-2 or Oberon.
Having used Simula (it was compulsory for CS classes at my university way back), the OOP in Simula is really quite different and at a far higher complexity level than Oberon.
Another comment listed influence directly on languages. But the influence on languages is far broader in terms of the simplicity of the languages being a facilitator for an explosion in language designs. It was seeing the ENBF for Pascal that got me to start experimenting with parsers and compilers, for example, because the simplicity made it clear that it was possible for a hobbyist to create their own from scratch. The existence of simple Wirth-language compilers made experimentation easy - I spent too much of my teen years looking at compiler source, and the Pascal compilers were easy to understand and modify. I wrote my first expression parser in Pascal, and my first code generator, and soon I ended up bootstrapping my own Pascal-inspired language.
Simplicity matters.
It's easy to forget that now when we have decades of hardware progress and orders of magnitude more RAM and CPU to work with, and decades of additional books and resources on how to build compilers using methods that were computationally infeasible at the time.
The existence of very simple compilers and languages has also been part and parcel of a lot of work at ETHZ that is not part of the Wirth languages themselves, but that was enabled by having access to simple languages as a foundation. Personal favourites of mine is work such as Franz' work on Semantic Dictionary Encoding, which led to his work on JVM JIT's etc.. Franz' student Andreas Gal came up with trace trees [1]. Brandis did fantastic work on showing a wide range of optimisations could be added to Oberon at a fraction of the complexity of the similar optimisations in gcc at the time. Mossenbock and Brandis did excellent work on SSA generation. Franz did simple but excellent work on protocol extension (dynamically propagating method overrides at runtime; I used parts of that in my experimental/unfinished Ruby compiler). There were many more. Some undoubtedly would have happened with more complex compilers to work on as well, but if you read the reports and dissertations from that period at ETHZ you also see plainly the influence that Wirth's approach carried over in the students subsequent approaches and focus on simplicity.
> a hypothetical "Turbo Algol" have been much different than "Turbo Pascal"?
It would have been slower and too big, and so lost a significant part of what made Turbo Pascal important.
In terms of implementation being more important, the speed etc. of Turbo Pascal depended heavily on the simplicity of compilation. Being able to keep the source, the compiler, the editor and the compiled object code in memory at once was a big deal facilitated by the extreme simplicity of the language.
[1] "Incremental Dynamic Code Generation with Trace Trees", Andreas Gal, Michael Franz, 2006: https://static.aminer.org/pdf/PDF/000/286/022/profile_driven...
I played with C parsers (using my own parser generator ... written in Modula-2 as it happens) back in '82 and never found the grammar overly intimidating. Of course you're familiar, but just do a quick scroll up and down to remind yourself of the length (or lack of it):
https://cs.wmich.edu/~gupta/teaching/cs4850/sumII06/The%20sy...
And it seems 25% of that is the expression syntax which really would be better (more efficiently) handled outside of the grammar via precedence climbing (which would also neatly handle Algol's user-defined precedence), and which would result in a smaller parser as well as faster one.
As far as I/O, not sure that Pascal had too much to crow about there ... you could open files, but not close them, no way to associate a filename with a file, a need to put any non-temporary file as a program parameter, the odd file buffer variable concept, with an equally odd syntax treating file types as if they were pointers to the element type...
A language that fascinated me at the time, but I never got to play with was Occam - designed specifically for the Transputer which was really way ahead of it's time and never caught on.
Other languages did simplicity better without sacrificing expressiveness by allowing natural extensions of the language. Wirth languages, by being Algol-derived, are overly-complex syntactically in ways that make them non-extensible, and possess ill-considered semantics on top of that.
So in Oberon:
WHILE i < 100 DO
...
END
(*Is i >= 100 here? Yes of course, otherwise the loop guard would be lying.*)
In C: while (i < 100) {
...
if (i >= 10) break;
...
}
/*Is i >= 100 here? No, not necessarily. Why is the loop guard lying then?*/Though I'm convinced that this ship has sailed, with go replacing oberon
https://github.com/modula3/cm3
It even comes with a GUI library, which even though developed in the 90th Stil, works on x11. I find that quite fascinating :-)
I'm pretty sure that both Mössenböck and Wirth would consider this a feature, not a bug.
But you are wrong if you think that Oberon-2 wasn't considered a competitor to C++. I'd say 1997 was early enough that it wasn't obvious yet that Java would snatch up such a big part of C++'s market share.
Wirth has always been very critical of software bloat and C++ is an abomination in programming language design. They certainly didn't set out to make an alternative to C++ but a good language that was an improvement over Oberon.
Hmm...considering Proebsting's Law[1], which is always shown to be too optimistic despite its pessimism[2][3], the arguments presented by DJ Bernstein[4] and the fact that Frances Allen got all the good ones in 1971 [5], etc. not sure how much this matters outside a few specialised domains.
[1] http://proebsting.cs.arizona.edu/law.html
[2] https://gwern.net/docs/cs/algorithm/2001-scott.pdf
[3] https://zeux.io/2022/01/08/on-proebstings-law/
[4] https://cr.yp.to/talks/2015.04.16/slides-djb-20150416-a4.pdf
And the latter is something we don't see as much these days, where people don't like "gaps", and compiled languages are measured by what they can't do (Go being one notable exception, it seems). For the Oberon system, Oberon was enough, and anything more would probably not have been worth it. Wirth and Mössenböck didn't really aim at meteorology super computers or whatever was the HPC rage in the late 80s/early 90s.
However there is a reason why it didn't stop at Oberon-2, and Component Pascal, Active Oberon, Zonnon and others followed up.
Not everyone at ETHZ agreed on the minimalism, and for some low level coding it was proven that having stuff like untraced references (introduced in Active Oberon, based on Modula-2+/Modula-3 work) actually made sense.
Latest revisions of Active Oberon also have some kind of generics support.
Most think of it as a "basic tinkering and learning" language, and indeed, that is probably the most common application. It does not have to be. [1]
UNIX came with C, and C became "IT".
[1] https://wiki.freepascal.org/Operating_Systems_written_in_FPC....
> not just the usual errors, inaccuracies and "common deviations" that you find in every C compiler, and that force you to secure libraries with #ifdefs.
And extension keywords for microcontrollers specific features, intrisics for CPU instructions, DSP specific capabilities,....
Yeah I am already used that what goes for Pascal gets another weigth when placed against C.
And while the discussions keep going around Pascal and its extensions, there is this little detail of Modula-2 being released in 1978, where all the issues with Pascal (as originally designed by Wirth) for systems programming were already fixed.
Where Apple II and III Pascal was based on UCSD Pascal, Lisa Pascal was apparently directly based on Wirth's P4 compiler, not on UCSD Pascal. See page 4.
> Therefore, Lisa Pascal lasted from 1981 to 1986, an eternity in the field of microcomputer languages.
;-)
> Tho (sic) Pascal will have the support of a small but vocal minority at Apple, C/C++ will be the dominant language for Apple and outsiders for the next decade.
RIP Pascal, you will not be forgotten - and you will live on as Delphi.
Two big extensions to standard/Wirth Pascal that enabled Pascal to work well for the Lisa and subsequent systems were units (separate compilation modules that could be imported/referenced using the USES statement) and an early version of Object Pascal known as Clascal.
I'm not sure that Pascal's evolution from Wirth Pascal to UCSD/Lisa/Turbo/etc. Pascal to Object Pascal and Delphi is something to complain about, any more than we'd complain about C evolving from K&R to C89/.../C23 and to C++ (and its various versions.) Pascal is still a capable and relatively compact language.
I think Pascal versions that lacked the @ operator and type casts could often use case variant records (relying on technically undefined behavior) to accomplish similar things.
I believe with (unsafe, packed) case variant records you could often get the address of anything that was allocated on the heap:
...
type ptrAddr = packed record
case Integer of
0: (addr: Integer);
1: (ptr: ^Something);
end;
var p: ptrAddr;
var somethingPtr: ^Something;
begin
new(somethingPtr); { allocate a Something }
p.ptr = somethingPtr; { copy its pointer }
writeln('Allocated something at', p.addr);
...
(Apologies for any errors - I don't write Pascal very often and haven't tested this.)This obviously relies on implementation-specific behavior such as integers and pointers being the same size.
With this sort of trickery you can do all sorts of (unsafe) things including pointer arithmetic, custom memory allocators, garbage collectors, (unsafe) type casts, (unsafe) low-level memory and memory-mapped I/O access, etc..
From which I suppose we can conclude that C/C++ without undefined and/or implementation-specific behavior has been found to be unsuitable for systems programming. Or that systems programs tend to be non-portable by nature.
Perhaps a more charitable conclusion is that languages like Wirth Pascal and K&R C (for example) contain (sometimes explicit and intentional) loopholes and unsafe/technically undefined behavior which can be useful for systems programming but problematic for memory safety and security/reliability.
Sadly Pascal standardization seems to have stalled after ISO Extended Pascal in 1991, while C is still evolving into C23.
Another conclusion might be that Apple started with UCSD Pascal (which was available for the Apple II and already included extensions to standard Pascal) and that they thought that certain extensions were useful even if they weren't technically necessary.
Clearly the Lisa didn't need Clascal or Object Pascal (the Macintosh didn't require them), but they provide advantages in terms of conciseness, modularity, reusability, etc..
cool!
> So far I only saw two of the ~350 Lisa OS source files looking like Clascal (both from the Lisa toolkit)
Have you looked for instances of SELF in the Lisa Toolkit and app sources?
Anything that is SELF.foo is a Clascal instance variable or instance method reference in a method. You can also use with SELF do... to make it more implicit like C++ or Java.
Also look for METHODS OF, SUPERSELF, CREATE, etc..
It's actually not a surprise I see some Clascal files in the Lisa OS tree since LIBTK in LISA_OS/LIBS is apparently just a copy of the Lisa Toolkit.
The USCD P-System spec is here, for anyone interested:
http://pascal.hansotten.com/uploads/ucsd/ucsd/UCSD_PASCAL_II...
Modules are good though - hence Units, Modula-2/3, Oberon, etc..
FWIW there was a huge following for the Wirth languages on the Amiga. I don't quite know how that came about but at times it felt like the programs written with Modula-2 or Oberon were as numerous as the ones written in C or Assembler.
That was mostly for Public Domain, Freeware, and Shareware software though.
Amiga system developed is very pointer heavy. Modula-2 could not prevent all issues but at least catch some. Made sense to me at the time. Plus I could do my computer homework in Modula-2 on my Amiga and then quickly rewrite it to Turba Pascal in school
The main improvement I see is the implied handling of multiple statements in an if-then-else-end. It seems like it would eliminate a lot of begin/end pairs, so I like it.
Wirth's decision to revert to Case sensitivity was a bad one.
With the exception of replacing "Uses" with "Import", it looks identical to Free Pascal/Delphi.
Both to semi-obscurity, but there is a lot to like in the Pascal language family in my opinion. Personally I find the syntax to be simple and attractive. Even though I spend a lot of time writing C-style languages, I don't have any trouble reading Pascal-style syntax.
My complaints about Ada and VHDL, which seem to have Pascal-inspired syntax, are they they feel a bit verbose compared to C++ and Verilog. On the other hand, Ada supports concurrency and memory safety, and has packages for dimensional analysis of physical units, and VHDL supports pluggable implementations.
One thing from Oberon linage that Turbo Pascal/Delphi/Free Pascal lack, is automatic memory management (with the exception for COM based types).
It's the best of both worlds. No GC pause, yet you can have huge strings in memory, return them from functions, all without issue.
http://progtools.org/article.php?name=oberon§ion=compile...
Oberon-2, a high performance alternative to C++ (1996) - https://news.ycombinator.com/item?id=13734438 - Feb 2017 (2 comments)
A basic forking server in Oberon-2 - https://news.ycombinator.com/item?id=11627697 - May 2016 (36 comments)
Oberon-2, a hi-performance alternative to C++ (1996) - https://news.ycombinator.com/item?id=3361469 - Dec 2011 (18 comments)
Jokes aside, I’m excited about any future additions to Fortran, esp. to make it appealing to direct data science use.
So we can't be sure if there really is no next Wirth around.
I think it certainly wasn't the norm back in the day. It's often easier to pile on existing solutions and try to go further by trying to cover up problems. Look how far we've come with the tooling surrounding C (e.g. static analyzer or runtimer fuzzers).
But sometimes you need to take a few steps back. It might not be obvious when looking at it today, but when Java came onto the scene, it had a tremendous effect on software engineering. It was a breath of fresh air that you could concentrate on other topics than how to prevent buffer overflows or what would be the best String class.
Java pushed modern programming practices in a way C or C++ just couldn't. I often joke that I am very grateful to Java as it saved me from working in C++.
But if you ask most programmers today, you don't get a very good opinion of Java. You probably shouldn't try to disrupt programming if you want to have rewards. :)