**preface** My priorities are safety, legibility, and expressiveness, in that
order. I can program in C and C++, but I much prefer _readable_ languages where
the compiler identifies as many errors as possible before you run it, because
debugging sucks, and debugging languages that thinks safety and readability are
signs of weakness is... better left unsaid.
_Languages I like_ (in alphabetical order)
**Ada** : C++ has spent 30+ years trying unsuccessfully to make C as featured
_and safe_ as Ada 83 (exceptions, generics, multitasking, ability to write
large, nontrivial programs without pointers, modules, ... oh, wait, C++ _still_
doesn't have modules, and on and on...) while still looking like C, because `a
= (b < c) ? d : e;` is so much more desirable than `a := (if b < c then d else
e);` or something. Ada 95 was the first ISO-standardized language with
object-oriented features. Ada 2012 has Design By Contract. A dialect of Ada
called SPARK offers the ability to prove a program meets specifications laid
out in DBC and/or pragmas. When Ada came out, people objected that it was too
complicated, so today we program with even more complicated languages that
offer fewer features, and no one knows Ada still exists and probably powers the
airplane you flew to that last FLOSS conference, so companies cannot find Ada
programmers, so they are switching to other languages which are less safe but,
hey, the kids coming out of universities these days don't like reading and
would rather match curly braces, so what can we do? (Yes, I have heard kids
coming out of universities say that they don't like languages that don't use
curly braces, with no further reasoning on the matter.)
**Kotlin** (please don't kill me): A better Java than Java. Lots less
boilerplate. Free & great tooling. The use of `in` and `out` types for
generics. A serious and almost successful attempt at `null` safety. I only
learned Kotlin because the choices in Android Studio are Java and Kotlin, so I
decided to try Kotlin I've never gone back to Java. Kotlin Native may or may
not turn out to be a thing one day, too.
**Modula-2** : Like others, I learned with Pascal; Modula-2 was Wirth's systems
programming language, and back in the early 90s the only options I had on an
Amiga were Modula-2, Oberon-2, and C. I couldn't find learning material on
Oberon (really) so Modula-2 it was. It was elegant, powerful, and -- given the
options -- the safest language available.
**Modula-3** : Modula-3 was from DEC, inspired by Modula-2 but different.
Surprisingly good, frustratingly boneheaded at times (you have to jump through
far too many hoops to instantiate a generic modules), but whenever I finished
writing a Modula-3 program, I always felt as if I'd had fun and wanted to do
more.
**Nim** : Do I have to explain this on a Nim forum?
_Language that was a sore disappointment_ (not asked, but some people are
writing this anyway)
**Oberon** : [Not to be confused with Oberon-2, which is similar] In principle
there's a lot to like, but after working through much of Wirth's Compiler
Construction textbook with Oberon I found myself increasingly frustrated by
choices that were made for the compiler writer's convenience, even when they
wouldn't have cost much. Maybe it was unfair because by the time I learned
Oberon I'd learned a lot of other OO languages, but, for instance:
* There is no way to initialize an object automatically; you must always
manually initialize, e.g., `o := NEW(T); o.Init;`
* Arrays of unknown size are allowed only as parameters to procedures, and
you cannot allocate an array whose compile-time size is not known except
through gruesome hacks. That is, you can do `TYPE T=ARRAY 10 OF INTEGER; VAR a
: POINTER TO T; BEGIN a := NEW(T); END` but not `TYPE T=ARRAY OF INTEGER; VAR a
: POINTER TO T; BEGIN a := NEW(T, 10); END` or something similar. Some Oberon
fans claim this defect is evidence of the language's brilliant design, but it's
important enough that at least two compilers I examined introduced a
`SYSTEM.NEW` command to do this... and each implemented it differently.
* In an attempt to minimize the language, some machine-level commands are in
the language proper, such as `LSL` and `ASR`, but others are consigned to the
`SYSTEM` module, which you are warned to avoid if you want to write portable
programs. In particular, masking an `INTEGER` requires you to convert it to a
`SET` via `SYSTEM.VAL`, use the `*` operator with another `SET`, then convert
back. All this just to avoid using the `&` operator on integers?
* Speaking of which, why `&` instead of `AND`? After all, `OR` gets its own
keyword. What happened to the aim of readability?
* In the command `o.method()`, `o` is not automatically passed to `method()`
as an argument, so you have to write `o.method(o)`, which grows really tiresome
after a while, especially if you use name variables according to their purpose
rather than just `s`, `o`, etc. (Amazingly to me, Wirth usually uses
single-letter variable names.)
* The language specification is maintained at Wirth's website. It keeps
changing, and often it seems as if the changes are done entirely for Wirth's
convenience. For example, he recently took up an interest in drones, and rather
than submit to his own medicine ("as simple as possible, but no simpler") [he
added a couple of
keywords.](https://inf.ethz.ch/personal/wirth/Oberon/Oberon.ARM.Compiler.pdf)
\-- **In fairness** , my understanding is that Wirth comes from a school of
thought where languages were supposed to be specified as minimally as possible,
but extended in practice, and he hated the complexity of Modula-2's compiler.
So, rather than add parallelism to Oberon, researchers at ETH developed an
extension called Concurrent Pascal.