Tracing where features and concepts come from in programming languages.
Or perhaps I should say "dislike?" No, I think "hate" will do nicely. All languages suck. — Steve Yegge
- Today's Big Three: Java, JavaScript, Python
- GC languages: C#, Kotlin, D, Go, Nim, Zig, Scala, Clojure, Swift, Dart, Haxe
- GC-less languages: C, C++, Rust
- FP languages: Haskell, Racket, SML, OCaml, F#, Elm, PureScript
- Scripting languages: Lua, Julia, R, Ruby, Matlab, VBA, Groovy, PowerShell, Erlang, Perl, PHP, TypeScript
- Declarative languages: CSS, HTML, SQL
- Forgotten languages: Ada, Awk, HyperTalk, Modula-3, Self, Smalltalk, Oberon, Algol 60, Algol 68, Cobol, Fortress, BASIC, Simula, Oz, Prolog, Dylan, NewtonScript, Delphi, PL/I, APL, Rexx
Wikipedia has a footer template with programming languages, and I thought it was a nice representative list:
Ada · ALGOL · APL · Assembly · BASIC · C · C++ · C# · Classic Visual Basic · COBOL · Erlang · Forth · Fortran · Go · Haskell · Java · JavaScript · Kotlin · Lisp · Lua · MATLAB · ML · Object Pascal · Pascal · Perl · PHP · Prolog · Python · R · Ruby · Rust · SQL · Scratch · Shell · Simula · Smalltalk · Swift · Visual Basic
Maybe the only thing I would contest is "Object Pascal" instead of Delphi, but that one is admittedly borderline.
This talk is great, and lays out the history of if-else. HN discussion.
In which I learn that the reason B and C use break
(as opposed to a separate keyword like endcase
) to escape out of switch
statements, is that B was based on an older version of BCPL, before BCPL got the endcase
keyword.
Interestingly, Perl 6/Raku re-introduced the difference with its succeed
keyword.
See https://github.com/masak/interesting-papers/issues/450 for details.
Its type and module systems were, according to this HN thread.
This timeline is a great resource and starting point for choosing influential languages.
According to Wikipedia,
Superplan introduced the keyword "for" resp. the German für with its for loop
Superplan was developed by Heinz Rutishauser, one of the earliest people deserving the title "computer scientist".
Among other contributions, he introduced several basic syntactic features to computer programming, notably the reserved word (keyword) for for a for loop, first as the German für in Superplan, next via its English translation for in ALGOL 58.
Two blog posts (one, two) by Bob Nystrom give a pretty good overview.
- External iterators tend to look like
Iterable
/Iterator
interfaces, and afor
loop syntax - Internal iterators tend to look like an
each
orforEach
method
Why numbering should start at zero argues both for counting from zero, and for using half-open intervals (like Python does).
The paper Programming Languages: History and Future has an awesome history chart on page 6. It's hard to do this chart justice. All the languages that survived to the present day are there, and a great many others besides.
- Dijkstra famously warned against short-circuiting logical operators, but in practice they are widespread in modern languages.
- A number of languages have both the non-short-circuiting form and the short-circuiting form.
- In more than one BASIC,
AND
andOR
do not short-circuit.
We should discuss this. A number of mainstream languages follow this convention:
- Perl 5/Raku
- Python
- JavaScript
Many others don't. It influences how you write conditions in the language, and spreads into other nearby idioms.
In this slide deck, there's a quote from Grace Hopper's "A Programmer's Glossary" (1954-05-01), which explains how we ended up with the term "compiler" crowding out the (previously more common) term "translator".
Well, in some languages, we did: the BASIC I grew up with does that (but overloads it with assignment depending on context). OCaml uses =
for equality testing. Pascal famously uses =
for equality and :=
for assignment.
It's a bit of a cliché already to call null pointers (or "null references") a billion dollar mistake. But maybe there's a core of truth and wisdom in there that can be focused on.
What could have been the alternative, if we imagine history playing out in a different way?
Pattern-matching and typed destructuring, for sure. That is, good match
statements, and also things like monadic combinators.
The problem with that is that this required (and still requires) a kind of type-based "regimentation" that people are still hesitant to commit to.
When people talk about ADTs, they mostly mean sum types (since product types are already common); and the sum types imply enforcing coverage (unless you're Erlang).
Maybe history took the path it did because enforcing coverage didn't look realistic, or didn't seem to have a good cost/benefit ratio or good ergonomics.
As statement terminators/separators? In many languages.