back
202 comments
The comments here are interesting, lots of possibilities for other ur-languages. My suggestion is macro based languages. Macro based programming predates all programming languages other than ASM[1]. The simple macro systems, like early assemblers provided, aren't ur-languages, but once macros can expand other macros and generate definitions of new macros, the macro systems can become general purpose programming systems.

The two earliest macro systems that were clearly designed to be general purpose languages that I know of are Christopher Strachey's GPM[2] and Calvin Mooers TRAC[3] programming language. These languages appeared at roughly the same time, the mid 1960s. I prefer the syntax of TRAC, but otherwise they are almost isomorphic. TRAC was featured in Computer Lib/Dream Machines[4] by Ted Nelson where the author said it was one of the three important languages for programmers to learn. A good introduction to TRAC and it's implementation can be found in Études for Programmers[5].

Other more contemporary examples of macro programming languages are m4, and TeX. LaTeX is programmed in the TeX macro system.

[1] Daniel Weise and Roger Crew, "Programable Syntax Macros", ACM SIGPLAN, 1993, https://dl.acm.org/doi/pdf/10.1145/173262.155105

[2] Christopher Strachey, “A general purpose macrogenerator,” Computer Journal, 8(3), pp. 225-241, 1965

[3] Calvin Mooers, "TRAC, a procedure-describing language for the reactive typewriter", CACM, Vol 9(3), March 1966, pp. 215-219, https://dl.acm.org/doi/10.1145/365230.365270

[4] Ted Nelson, "Computer Lib/Dream Machines", 1974, Self-published. (There is a 2nd edition from Microsoft Press, but I'm only familiar with the 1st edition).

[5] Charles Wetherell, "Études for Programmers", 1978, Prentice Hall. (It's out of print and available from Amazon for $427. I'm going to have to start locking up my old books.)

Macro/concatenative languages should replace the Forth line. The author is off-base in thinking RPN or stacks are important to Forth rather than concatenativity. RPN is just an easy way to make interpretation simple, an implementation detail, not a functional requirement. It would be like defining APL as being evaluated right-to-left.
I read about TRAC in Computer Lib while working on a template language that basically just has #! as a special prefix in JSON objects to signal a macro-expansion. I expand macros until there are no more macros to expand, and return that as the result to render in HTML. Reading about TRAC is what made me realize I had a general purpose language on my hands. Too bad, I could have avoided the whole exercise of writing an interpreter, scheduler, debugger if I just left it as an alternative syntax for interpolating variables into HTML.

Thanks for the tip on Études for Programmers, looks like I'll be able to look at it in a library at least, surprised archive.org didn't have it.

The last two books are available at your favorite site.
Pretty excellent summary, this is roughly the taxonomy I have in my head.

I would maybe add SQL as an ur-language as well. It's not quite general purpose like most of these, but it should have a place in this list, I think. It has some kinship with Prolog and the declarative style, but it's really it's own thing.

You could also maybe argue for something like LabView. Many programmers look down on purely graphical programming languages, but with Houdini, Unreal's Blueprints and the various node-based shader/material systems in gamedev, I think it probably deserves it's own little branch of this family tree.

SQL is on the same group as Prolog. In fact, it is a simpler and purer implementation of a constraint-solving language, so it is probably a better option for learning the group than Prolog.

For the signal transformation ones, there are also the hardware definition languages on the same category as LabView and animation languages.

Not to mention the various graphical programming languages used in industrial control - ladder logic, continuous function chart, and sequential function chart. Ladder in particular is incredibly widespread, as it was a direct descendant of the diagrams used to document and debug relay logic, and was adopted in some of the first PLCs (programmable logic controllers) in the late 60s/early 70s.

Programming PLCs sometimes feels like going to, say, Australia or New Zealand, and being immersed in an environment which went down a different evolutionary tree very on in its development.

SQL might be in its own category or not, but it shares a trait that languages in other categories ended up with: "I know, we'll make it kind of like writing English so that people who aren't experts can program!".

This is one of those things that seems to keep coming back - recently in the Ruby world with Cucumber.

I think you’re first instinct is correct given libraries like Linq and GQL in Groovy. The relational algebra is decidedly different versus say map, filter and reduce.
Sql is accidentally general purpose. Recursive queries, a feature present in all implementations since the 90s, put it just over the line of turing completeness.
IMO the eighth ur-language is Erlang (with cousin Elixir), which brings the ultra-scaling actor model, an abstract operating system designed for high reliability and low latency, and a bunch of nice rare features like builtin binary structuring/destructuring and pattern matching. While it had its origins in prolog, it's now sufficiently far away from prolog's inner machinery that it only has syntactical similarity.

Learning erlang made me definitely at least 10% smarter as a programmer. Highly recommend, even if you never use it in anger.

I think reactive/synchronous languages [1] deserve a category of their own. They share little/no overlap with any of the others. My background in electrical engineering biases me to Verilog, VHDL, and LabVIEW as exemplars, but there are many others. The distinguishing characteristic of the category is that programs are effectively declarative functions of time and can be composed as such, much like electronic circuits.

(I categorized the temporal declarative language TLA+ into the Prolog family in another comment -- a main distinction here is, though one can express reactive relationships in TLA+, the intent of the language and design of the TLC model checker is that such relationships are only usable for verification code, whereas implementation code must be written as state-succession pairs. A true reactive language permits and encourages both styles of coding for implementation code -- e.g. Verilog's = and <= operators. Vice-versa, a true logic language includes nondeterminism as a core construct, whereas reactive languages typically do not.)

[1] https://en.wikipedia.org/wiki/Synchronous_programming_langua...

An article like this is hard to write. This is a good one. I have a few quibbles, but they are just quibbles.

IMHO* a distinguishing feature of Algol like languages (aka "procedural" languages) is the distinction between expressions and statements. Though personally I've never seen the appeal, that distinction seems to be popular for some reason.

* this isn't even a quibble -- the article is fine without it. Just something that has always seemed weird to me.

My big quibble with this is that the OO category is way too narrow. The way it is worded it excludes almost all languages we'd currently consider OO, and is so strict it largely makes the category pointless. Loosen it to exclude the "You do conditionals..." bit, and focus on the message passing, and e.g. Ruby and other languages that are message passing focused to the point of it tending to dominate semantics over it's Algol-related syntax fits firmly in this category.

Listing Self rather than Smalltalk as the basis of OO languages is also a bit odd. Calling it a "purer form" I guess is a justification, but the other view is once you losen the definition as above, self is just the root of one small (albeit influential, via Javascript, branch of OO languages that almost all owe more to Smalltalk than Self. If another language than Smalltalk should be at the root, it should be Simula, for inspiring message passing, not Self.

I think this taxonomy is interesting, but I have some quibbles on the characterization of the languages I use the most.

If I were to put Ruby into one of these categories, I would place it first under Self (the object-oriented languages). Ruby is the most object-oriented language that I've ever used in that everything is an object that you send signals to. Even classes in Ruby are objects (they are instances of the `Class` class). Ruby was explicitly inspired by Smalltalk, one of the two exemplars cited by the post.

After the "Self" category, Ruby would fit better under the "Lisp" family than the "ALGOL" family because of Ruby's deep metaprogrammability.

I'm guessing the author was fooled by the availability of C-style `for` loops in Ruby, but that's generally not how Ruby programmers write a loop. It's much more common to write `list_of_things.map {…}` or use any number of other iteration methods available through the `Enumerable` module.

JavaScript to me fits better under the ML (functional languages) family than "ALGOL". The first-class nature of JavaScript functions is the core feature of the language. Of course, if you define "functional languages" by having static type systems this grouping wouldn't work for you. But for me it's all about the functions. You can pass functions around and return them from other functions. You can write utility functions to memoize or otherwise transform functions.

And while not everything in JavaScript is an object the non-object values in JavaScript have "object" versions. JavaScript still has some object-oriented chops. Functions are themselves objects, and while method calls are usually just reading a function off of an object and calling it, you can intervene in the property-reading step to enforce a more "message passing" style.

I'm guessing that most developers will have similar quibbles about the categorization in this article of the languages they are most familiar with. But this is still an interesting frame of reference. And if you only work in languages that fit squarely in the "imperative" category (or write code in an imperative way), I encourage you to explore some of the others.

>Every programmer needs to know a language in the ALGOL family well. Once you do, then it’s worth branching out. Learning a new language that traces to an unfamiliar ur-language each year will pay dividends

I second this. I would consider myself an intermediate level programmer and learning Scheme (via the excellent book "Structure and Interpretation of Computer Programs") took my programming to a new level and made me think of programming from a completely different angle.

Agree on the families, but I would pick a different representative for many of the categories.

Algol -> C. Mostly because you can actually do things with C, and yet it remains a fairly small language that's a relatively pure exemplar of the Algol tradition.

Lisp -> Scheme. Also because it's a tiny language that tries to push the fundamentals of the Lisp family (code-as-data, recursion, functional programming, macros) as far as possible.

ML -> Haskell. ML is eager, Haskell is lazy. If you're going to learn about the ML family, you might as well learn the concept of lazyness, which results in a very different style of programming than FP of the Scheme variety (in the Lisp family).

APL -> J. The usage of special symbols is largely irrelevant to the concepts in APL, and it's a barrier to accessibility. You can learn all the important parts of array-oriented programming with J and use actual words to do it.

Self, Forth, and Prolog I would keep as exemplars of their type. I would also add TCL as another ur-language for string-based scripting languages (with Perl, PHP, and SNOBOL as other representatives of the category).

> A reader pointed out to me that getting Forth in a deep way usually involves building Forths, since they’re small enough for a single person to build one from the ground up fairly quickly.

I would say the same about Lisp.

In fact, each list in Lisp is like its own little stack in Forth. Prepending an element to the front of a list is like pushing onto a stack. Separating the first element from the rest of a list is like popping from a stack.

Lists of lists & atoms are like stacks that can contain references to other stacks, as well as atoms.

Years of bitter personal experience have taught me that FORTRAN is the real ur-language in the sense that a suitably determined programmer can write FORTRAN code in just about any language. I have certainly had to debug this code all over the place.
I've never heard of ALGOL both predating and influencing FORTRAN. FORTRAN was announced as a product at the Western Joint Computer Conference in 1957 [1]. ALGOL was developed at a meeting in Zurich in 1958 [2].

[1] https://www.softwarepreservation.org/projects/FORTRAN/paper/...

[2] https://en.wikipedia.org/wiki/ALGOL

I think there are additional examples, where the syntax isn't necessarily wildly different, but the paradigm is

* Shader code: is ostensibly C, but fundamentally different runtime characteristics.

* Dataflow (or Signals/Excel): Topological graph of computation.

* HCL (or Cloudformation/CDK/etc): Connecting execution via a different runtime.

* React Hooks

IDK it just seems like there is a lot.

It is sort of angels dancing on pins but who came first between cobol, Fortran and Algol is curious. Even this article seems to be putting Algol afterwards. It was a more collective language, but I didn’t see it penetrate as much as remain a discussion topic. Perhaps the thread through C gave it entrance?

And then there’s basic…. Maybe that is just pidgin, giving those who could not speak at all some words?

> Lisp is about a year younger than Fortran, which makes it the second oldest language still in use today

I disagree with this line since Lisp is not really a "language", but a family of them. If we consider Clojure and original Lisp to be the same language, we should also consider that to be true of Algol and Rust.

> A lot of work was done on how to make Smalltalk run fast and efficiently, culminating in the Strongtalk project.

The Strongtalk technology also came from Self; it was thought that Self would be too hard to make fast, until it wasn't.

Very useful for an intermediate programmer who appreciates the craft and wants to extend their skills by becoming a true polyglot.

As for total beginners, it can be demeaning to explain why asking which language of the ALGOL descendants to learn is a pointless question. For their case, I don't bury the lead: I tell them "They all have the same lineage and are fundamentally similar, so just pick the one you'll learn the most robustly with. Most everything else is learning libraries."

I'd add probabilistic languages like Figaro.
The claim that Fortran and assembly languages "trace to" Algol is... odd. Fortran I was released in 1957; the first fragmentary spec for Algol wasn't released until 1958 (with Fortran project leader John Backus as one of the primary contributors). And there were also recognizable assemblers, in the modern sense, at the time (although terms like that weren't always used with their modern meanings in the 1950s, which lays up all sorts of traps for the unwary).
There’s a missing “minimal language” with a combination of features that doesn’t yet exist, but should:

- value semantics with both implicitly-copyable and move-only values

- unboxed generics with type classes/traits/protocols and associated types

- as little syntax and sugar as possible for everything else

Well, a bit arbitrary, especially the choice of Self as the "ur-language" of object oriented programming.
I take issue with calling stack languages the inverse of Lisp. This isn't really the case since functions in stack languages are fixed arity, while Lisp functions are not. The inverse of RPN would be the fixed-arity prefix notation used by the REBOL family of languages.
I think you could make a case for including Ada and/or COBOL, separately from ALGOL. Ada is/was an early attempt to remove errors from programming, and COBOL was an attempt to make a "business language" that would be understandable by suits.
I despise this writing style. What is the point the author is trying to make? Define what a ur-language is up front, don't be so damn coy. This isn't much more than a list of interesting languages for some definition of interesting.
Ahh this is good. But what are ur-languages really? Can we tell when we have counted all of them? Is the collection determined more by the application domain (the problems we think are important to solve) or the architecture of Von Neumann type computing)? E.g. I don't know much about quantum computing but I suppose there is an ur-language associated with it.

Whenever I see a taxonomy I itch for some underlying logic that unifies them. If the explanatory scheme is successful then you might actually convert the taxonomy into a tree, where the root is some property that all of them ur-languages share etc.

Objective C is a good example of a Smalltalk derived language that was in heavy use for a while. Even though it's technically a superset of C, in actual use it's more like a message passing language.
Ruby is mostly a Self in this classification, right? Or maybe a Self/ALGOL hybrid. It does have iteration as a method (.each), but also has traditional if statements.
Most of these are too old for me but ive always had this idea of fundamental language paradigms too.. my ur-languages are maybe Python, Lisp, Haskell, SQL. If you drop the requirement to choose the oldest language in each paradigm (which is more practical from a learners POV), there are many sets of basis vectors (languages) that cover the entire vector space (all language features).
I think mentioning Simula would be fair. The first object-oriented language.

"" Simula (1967) is generally accepted as being the first language with the primary features of an object-oriented language. It was created for making simulation programs, in which what came to be called objects were the most important information representation.

I feel like C++ needs to go into its own category, seemingly infinite features with only thinly veiled illusionary safety.
So why is it called "ur"?
I also think a lot of programmers could get a sort of enlightenment by getting very proficient in SQL. (To the point where you can do general programming in it using recursive CTEs if you have to, even though probably impractical for real use cases.)
I would add bitcoin script to the Examples section for Forth. That's a very notable application of Forth in modern usage.

Also maybe command / shell languages deserve a mention?

Interesting to see Erlang mentioned in the Self category instead of the Prolog category, but it makes sense given how much it borrows from both.
Might add Lucid in here as a dataflow ur-language.
I would love to see a mapping of languages to features. I recognize this might be a large table.
Where does Excel fit into this classification?

;-)

Where does TLA+ and PlusCal fit in?

They do not match any of the listed families, do they?