48 comments

[ 5.3 ms ] story [ 69.4 ms ] thread
Reminds me a bit of Bruce Tate’s approach in 7 languages in 7 weeks, which is where I first encountered Erlang.

I think from a historical perspective, describing COBOL and Fortran as part of the ALGOL family is a stretch, but I suppose it’s a good reminder that all history is reductive.

Another direction to explore logic languages is Datalog.
- Algol 68 docs: https://algol68-lang.org/resources 'a68g' it's a free as in freedom compiler.

- Forth: you can use PFE,Gforth for ANS Forth requeriments. Or EForth if you reached high skills levels where the missing stuff can be just reimplemented.

EForth under Muxleq: https://github.com/howerj/muxleq I can provide a working config where a 90% of it would be valid across SF.

Starting Forth, ANS version: https://www.forth.com/starting-forth/

Thinking Forth, do this after finishing SF: https://thinking-forth.sourceforge.net/

Also, Forth Scientific Library. You can make it working with both GForth and PFE, just read the docs.

Full pack: https://www.taygeta.com/fsl/library/Library.tgz

Helping Forth code for GForth/PFE. If you put it under scilib/fs-util.fs, load it with:

    s" scilib/fsu-util.fs" included


https://www.taygeta.com/fsl/library/fsl-util.fs

- Lisp. s9fes, it will compile under any nix/Mac/BSD out there, even with MinC.

S9fes: http://www.t3x.org/s9fes/

Pick the bleeding edge version, it will compile just fine.

For Windows users: MinC, install both EXE under Windows. First, mincexe, then buildtools*exe: https://minc.commandlinerevolution.nl/english/home.html

Then get 7zip to decompress the s9fes TGZ file, cd to that directory, and run 'make'.

Run ./s9 to get the prompt, or ./s9 file.scm where file.scm it's the source code.

In order to learn Scheme, there's are two newbie recommended books before "SICP".

Pick any, CACS, SS, it doesn't matter, both will guide you before SICP, the 'big' book on Scheme:

Simply Scheme https://people.eecs.berkeley.edu/~bh/pdf/

Simply.scm file, select from ';;; simply.scm version 3.13 (8/11/98)' to '(strings-are-numbers #t)' and save it as simply.scm

https://people.eecs.berkeley.edu/~bh/ssch27/appendix-simply....

Concrete Abstractions

Book:

https://www.d.umn.edu/~tcolburn/cs1581/ConcreteAbstractions....

The SCM files needed to be (load "foo.scm") ed in the code in order to do the exercises:

https://github.com/freezoo/scheme-concabs

If you are en Emacs user, just read the Elisp intro, it will work for a different Lisp family but with similar design.

Spot the differences:

Scheme (like s9):

    (define (square x)
     (* x x))
We try:

    >(square 20) 
    400
Elisp/Common Lisp (as the web site shows):

    (defun square (x)
     (* x x))
Same there:

     >(square 20)
    ...
(2022) and unfortunately advice to spend significant amounts of time in learning multiple languages is becoming rapidly redundant in the LLM age.
I might add another class of languages: those intended to express proofs, via the Curry-Howard correspondence. Lean is a primary example here. This could be considered a subclass of functional languages but it might be different enough to warrant a separate class. In particular, the purpose of these programs is to be checked; execution is only secondary.
there's a few more semantic families: verilog, petri nets and variants, Kahn process networks and dataflow machines, process calculi, reactive, term rewriting, constraint solvers/theorem provers (not the same with Prolog), probabilistic programming,

plus up and coming (actual production-ready) languages that don't fit perfectly in the 7 categories: unison, darklang, temporal dataflow, DBSP

It may feel like a little bit of cheating mentioning the above ones, as most are parallel to the regular von Neumann machine setup, but was meaning for a while to do an article with 'all ways we know how to compute (beyond von Neumann)'.

My favorite subject when studying CompSci (TU Delft) was called "Concepts of programming languages". We learned C, Scala (for functional) and Javascript (prototypes).

It made learning Elixir years later much easier.

We also had a course that basically summed up to programming agents to play Unreal Tournament in a language called GOAL which was based on Prolog.

For years I've wanted to use Prolog but could not figure out how. I ended up making a spellcheck to allow LLM's to iterate over and fix the dismal Papiamentu they generate.

This article is full of gross mistakes. For example it claims that Caml is "Cambridge ML" which is ridiculously false. Fact check every sentence. Really sad.
One correction I'd make to the article's taxonomy: Ruby is an object oriented language not an Algol. Its inspiration is Smalltalk, and much of the standard library naming comes from that route (eg collect rather than map).

Ruby is object oriented from the ground up. Everything (and I do mean everything) is an object, and method call is conceived as passing messages to objects.

While Ruby is most often compared to Python (an Algol), they come from very different evolutionary routes, and have converged towards the same point in the ecosystem. I think of Ruby as a cuddly Alpaca compared to Python's spitting camel.

Both object oriented and an Algol.
I wrote something similar here: https://fmjlang.co.uk/blog/GroundBreakingLanguages.html

We agree on Algol, Lisp, Forth, APL, and Prolog. For ground-breaking functional language, I have SASL (St Andrews Static Language), which (just) predates ML, and for object oriented language, I have Smalltalk (which predates Self).

I also include Fortran, COBOL, SNOBOL (string processing), and Prograph (visual dataflow), which were similarly ground-breaking in different ways.

Folks might find the following useful for studying PLs;

1) Advanced Programming Language Design by Raphael Finkel - A classic (late 90s) book comparing a whole smorgasbord of languages.

2) Design Concepts in Programming Languages by Franklyn Turbak et al. - A comprehensive (and big) book on PL design.

3) Concepts, Techniques and Models of Computer Programming by Peter Van Roy et al. - Shows how to organically add different programming paradigms to a simple core language.

Lots of us are having fun identifying our choice for missing family :)

One I might suggest is scripting languages, defined loosely by programming tools which dispatch high-level commands to act on data pipelines: sed, AWK, the sh family, Perl, PowerShell, Python and R as honorary members. In practice I might say SQL belongs here instead of under Prolog, but in theory of course SQL is like Prolog. Bourne shell might be the best representative, even if it's not the oldest.

AWK et al share characteristics from ALGOL and APL, but I feel they are very much their own thing. PowerShell is quite unique among modern languages.

C++ has Algol roots, but I think the C++ template metaprogramming style is an ur-language of its own. You could draw some parallels with ML maybe, but they came at it from a different direction.
(comment deleted)
I’ve very slowly been trying to do the “99 problems” list in each of these languages groups. It’s been a fun experience seeing the differences. Though I think I would need a larger, less algorithmic, project to really see each group’s strengths. Especially for the OOP group.

One thing the article didn’t touch on was SmallTalk’s live visual environment. It’s not a normal source code / text language.

> It’s not a normal source code / text language.

Do you think source code cannot be compiled and run from the command-line?

https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

I’ve never worked with SmallTalk professionally, so I could be mistaken. I know you can write/compile SmallTalk source code. But my understanding is that this wasn’t the way it’s intended to be used. I am under the impression that the live images were how the language designers intended it to be used. And that the live images are a better representation of the OOP/message passing paradigm.
Wasn't the way it's intended to be used to do what ?

"Within each project, a set of changes you make to class descriptions is maintained. … Using a browser view of this set of changes, you can find out what you have been doing. Also, you can use the set of changes to create an external file containing descriptions of the modifications you have made to the system so that you can share your work with other users."

1984 "Smalltalk-80 The Interactive Programming Environment" page 46

    ~
"At the outset of a project involving two or more programmers: Do assign a member of the team to be the version manager. … The responsibilities of the version manager consist of collecting and cataloging code files submitted by all members of the team, periodically building a new system image incorporating all submitted code files, and releasing the image for use by the team. The version manager stores the current release and all code files for that release in a central place, allowing team members read access, and disallowing write access for anyone except the version manager."

1984 "Smalltalk-80 The Interactive Programming Environment" page 500

I always enjoy these summaries. I took my bachelor of computer science in the early 1990s. It covered a language in most of these categories.

We didn't learn APL (Who is teaching the use of those custom keyboards to 100s of young students for one semester?)

The processing power of systems at the time made it clear which language classes were practically useful and usable for the time and which were not.

Prolog ran like a dog for even simple sets of logic.

We had the best internet access and pretty powerful desktop systems for the time.

I'm still curious why we didn't learn smalltalk. Could have been the difficulty of submitting and marking a system in a particular state rather than a file of code :)

Virginia Tech at least used to - the school of Architecture had a programming in APL class.
That's amazing. I used to live under an architecture student (our building). His command of design history was great. His command of maths? Well, not so much.
A Smalltalk implementation provides:

    Smalltalk VM 

    Smalltalk image file 

    sources file (plain-text original source code file) 

    changes file (plain-text change log, initially empty)
So there are plenty of ways to submit code to be marked.

See "OU LearningWorks: a customized programming environment for Smalltalk modules"

https://ieeexplore.ieee.org/document/841064

Isn‘t FORTRAN also a ur-language? It was invented in 1957.
Most old-timers here are familiar with a Prolog-variant: make. Anyone who's struggled over a complex Makefile wishes they had a more sane declarative language!
I would add another to the list, which is languages where every expression yields zero or more values, particularly `jq`. there are some antecedents in Icon and xquery, but these generally require explicitly opting into either production or consumption of value streams, where jq does this stream processing automatically from the ground up. (icon requires use of a suspend and needs an every clause to walk the generated values, xquery requires explicit 'for' statements over streams as many builtin operators fail on value streams)

in jq, the comma separates expressions, which independently yield values. a span of such expressions is called a 'filter', since they are always run by passing values from the prior filter into them (with the initial values sourcing from json objects on stdin, or an implicit null if you pass -n to the program).

    $ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; x, y '
    "a"
    "b"
    "c"
    1
    2
    3

    $ jq -c '. + 10, . + 20' <<< '1 2 3'
    11
    21
    12
    22
    13
    23
brackets collect values yielded inside of them.

    $ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; [x,y] '
    ["a","b","c",1,2,3]
if you have a complex object that includes multiple expressions yielding multiple values, construction will permute over them.

    $ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; {"foo": x, "bar": y} '
    {"foo":"a","bar":1}
    {"foo":"a","bar":2}
    {"foo":"a","bar":3}
    {"foo":"b","bar":1}
    {"foo":"b","bar":2}
    {"foo":"b","bar":3}
    {"foo":"c","bar":1}
    {"foo":"c","bar":2}
    {"foo":"c","bar":3}
the pipe operator `|` runs the next filter with each value yielded by the prior, that value represented by the current value operator `.`.

    $ jq -nc ' 1,2,3 | 10 + . '
    11
    12
    13
    $ jq -nc ' 1,2,3 | (10 + .) * . '
    11
    24
    39
binding variables in the language is similarly done for each value their source yields

    $ jq -nc ' (1,2,3) as $A | $A + $A '
    2
    4
    6
functions in the language are neat because you can choose to accept arguments as either early bound values, or as thunks, with the former prefixed with a $.

for example, this runs `. + 100` parameters context, with `.` as the 10,20,30 passed to it:

    $ jq -nc ' def f($t): 1,2,3|$t ; 10,20,30|f(. + 100) '
    110
    110
    110
    120
    120
    120
    130
    130
    130
where this runs `. + 100` in the context of its use inside the function, instead receiving 1,2,3:

    $ jq -nc ' def f(t): 1,2,3|t ; 10,20,30|f(. + 100) '
    101
    102
    103
    101
    102
    103
    101
    102
    103
so you could define map taking a current-value array and applying an expression to each entry like so:

    $ jq -nc ' def m(todo): [.[]|todo] ; [1,2,3]|m(. * 10) '
    [10,20,30]
it's a fun little language for some quick data munging, but the semantics themselves are a decent reason to learn it.