Hello again my fellow gopherites!

I've been saying for a while that I'd write a phlog post on
programming languages and garbage collection and such, and this is
that post :)

Programming languages are things that attract a lot of strong
opinions and as such I was kind of reluctant to phlog about this,
but there's been enough times that the subject comes up during
screwtape's fantastic anonradio show, that I'd like to elaborate
on a few things, as I can go into a little bit more detail in a
phlog post than I otherwise could in sdf com chat.

,=============================,
| First, a bit of background: |
`============================='

I've been programming for a while. I wrote my first lines of code
at a very young age (like 7 or 8) on a commodore 64. Like many who
started in that era, I have fond memories of typing listings out
of the fantastic c64 manuals and magazines that were around at the
time. I vividly remember the rush of excitement I felt when that
famous balloon sprite appeared on the screen, which also kicked of
a new hobby of drawing sprites on grid paper, adding a bunch of
numbers, entering them as DATA lines in basic, and creating custom
sprites. It was a lot of fun. Eventually I also got my first
introduction to simple assembly programming on the c=64 when I
realized the source code for a lot of games was just a single
'SYS' instruction. It made no sense until one day I accidentally
entered the monitor mode of the final cartridge and started poking
around. Eventually my mom brought home a decommisioned 386 PC from
work, which had Pascal on it (Just pascal, not turbo pascal), and
a new world of amazing potential opened. Eventually we got a 486,
and I upgraded to turbo pascal, and soon enough I got my very own
PC, a pentium 75 MHz, and started using c++. While professionally
I had been using various languages, between php, python, c++,
perl, etc,... for my personal projects I always preferred c++,
mostly because it strikes a balance between having the features I
like and being widely supported on enough platforms. Not so much
because I like it syntactically. In fact, the ever changing and
evolving idioms are something of nightmares, really,... :)

As screwtape mentioned in his last show, I did spend some quality
time with smalltalk, mostly because of the Croquet project
(later OpenCobalt), which was a very interesting and exciting
concept to me at the time. If you're not familiar, the idea was to
have a soft of interlinked multi-user programmable
3D-environments. You could drop 3d objects into the world, program
their behavior with smalltalk, in real time, and have other people
walk through your world, and worlds could be inter-connected by
portals you could walk through. It is actually remarkably similar
to vrchat today, if vrchat were user-scriptable in real-time. You
could live-code an object, drop it in the world, and everyone
would instantly see your hand-coded thing. Unfortunately, the
people behind it were looking to monetize it and started marketing
it more as some sort of corporate collaborate environment thing.
They made a vnc viewer window thingy where multiple users could
share desktop control in some sort of virtual meeting.
Incidentally, this was way before zoom and teamviewer and the
likes were a thing. But even though that was a neat use-case, it
was way under-selling the capabilities of the platform. But alas
the platform also had a lot of bugs, and it was difficult to get
going. Soon enough development started getting a little neglected,
and now it's just yet another ambitious project that died on the
smalltalk vine.

,============================================================,
| My language preferences and why they are the way they are: |
`============================================================'

Anyhow... my language preferences: I tend to prefer verbose,
strict and statically typed languages. The more problems that can
be caught at compile-time, the better. That's not to say that
run-time error handling and checking doesn't have it's place.
I'll elaborate in the sections below.

On strictness and static typing:

Strict static typing to me is a language feature that helps me
catch mistakes as early as possible (at compile-time) as opposed
to when it's too late (at run-time). If you've got some sort of
long-running software, say a daemon, that people rely on always
being available, a hard runtime crash can be a big problem, as it
would take the entire thing down. But, at the same time, if I were
to ignore all errors to keep the application running no matter
what, one could easily enter a state where things are kind of
broken, but nobody notices, because the application is still up,
and in the worst case scenario you end up losing or corrupting
data. That's arguably worse than it actually going down hard. Not
to mention it's harder to figure out what exactly broke this way.
If an application fails fast and hard, a backtrace ought to show
exactly where things went wrong. This makes things a lot nicer
for debugging. Obviously both hard and soft failures described
above are bad, and best avoided at all costs. Hence the preference
for compile-time checks over runtime-checks. That's not to say
there isn't value in all 3, depending on the situation, and as
such it's not uncommon for me to write my c++ programs in such a
way where if the software is compiled in Release mode, the main
function has a try-catch which logs errors and tries to keep
things up, where-as a debug build, fails hard and fast.
Being able to change that behavior with the preprocessor, is a
feature I quite like about C++ -- not that there isn't perils with
the approach as now you're technically producing 2 behaviorally
different programs from the same source code. However, language
wise, you could do worse, as there are many languages (like most
of the interpreted ones) where runtime checks are the only option.

There is also a maintainability and readability aspect to strict
typing. If it is mandatory to declare all variables with a type,
this informs the code reader of the intent of the programmer. You
can visually see what this thing is not just from it's variable
name, but also from it's type, which tells you STRUCTURALLY what
this thing is, not just conceptually by name (assuming the
programmer even gave the variable a descriptive name to begin
with). As such I strongly dislike languages with inferred types,
and I'm not terribly happy that modern c++ introduced the 'auto'
keyword, and I tend to use it very sparingly, if at all.

On readability and maintainability:

People often joke about APL, how it is near unreadable, and looks
like a bunch of hieroglyphics. What actually makes APL so
difficult to parse at sight, is not necesarily it's use of funky
symbols, imho, but rather it's density.
If a single character has a lot of meaning, especially wrt program
flow, it becomes very hard, if not impossible to visually see how
a program might behave when executed. This is also incidentially
why indentation makes code more readable, as execution branches
become visually distinct that way. Code density is the enemy of
readability and maintainability. As such, I like very verbose
languages that make execution branch blocks very visually
distinct.

On garbage collection:

I am mostly indifferent about gc. It is another tradeoff. I like
the C++ STL smart pointer objects quite a lot, as they not only
help manage memory, but also inform the reader of the code on the
ownership of an object, and in turn, the conceptual and structural
relationship between objects. Once you grasp when and how to use
shared pointers, unique pointers, and weak pointers, you don't
need gc at all. But there is a learning curve associated with these
objects and idioms. Similarly, while a gc might help the casual
programmer get going faster, at some point you will have to delve
into the guts of your garbage collector when you hit performance
issues while dealing with your massively parallel problem du-jour,
for instance, and you'll have to learn about it's different gc
algorithms, how to configure them, how and when to manually
trigger a gc, etc,... you will be having to learn all the details
and innards of the magic black box that is your gc. Either way it
is a learning curve, either way it is complexity. It's just moved
elsewhere and it's obfuscated. That doesn't negate that gc is
certainly useful and convenient for the general use case.
Generally I don't mind having it, provided it's got enough control
mechanisms to deal with the more uncommon situations.

On security:

Ever since the rust community was unleashed upon the interwebs, a
lot of people have unleashed their often strong, sometimes
uninformed, opinions about memory safety. While memory safety is
obviously important, it's also not the MOST important. Most modern
operating systems have various protection mechanisms such as ASLR,
stack overflow protection, etc,... that actual practical
exploitation of these bugs is very difficult if not impossible.
Nonetheless, bundling C++ in the same bucket with C on this is a
bit unfair. While the stl smartpointers don't make such bugs
impossible, they make them much more rare (you have to be really
be trying hard to be dumb^H^H^H^Hunsafe), between static code
analysis and ASAN, there are a lot of tools that will catch these
problems before they are a problem - you have to use them of
course. At the end of the day, any language that is flexible
enough to allow you raw memory access, is going to have some of
these problems. And as always, you are free to trade off some
flexibility for security, and vice-versa. Personally I think c++
strikes a good middle-ground in this. Not that c++ is a prime
example of a good language.

On OOP vs functional:

I guess I am also mostly indifferent on this, although I find it
useful to have both mechanisms available, and as such I think a
good language should offer facilities for programming in both
styles. Sometimes one is more suitable than the other. I tend to
lean more towards OOP, as it enforces a particular way to
structure code, that is easy to recognize, especially when reading
other people's software. I've had to work on some purely
functional software other people wrote, and you either have a lot
of functions calling other functions, or you have a lot of nested
closures. In the former, things don't look all that different from
the heavily criticized old basic spaghetti code. In the latter,
deeply nested closures also make it hard if not impossible to
figure out what's going on without a lot of reverse engineering.
That's not to say there's not clean ways to organize and write
functional code. I observe a lot of people with a math-y
background instead of a software background tend to favor
functional over OOP. If for these people a rats nest
of ((()))()))))((()())))())) is more readable to them, more power
to them! ;)

,===============================,
| Examples of languages I enjoy |
`==============================='

Translating all this to actual languages:

Because I like strictly typed, verbose languages, I've always been
quite fond of the Wirthian languages. After Niklaus Wirth invented
Pascal, he came up with a series of similar languages, that are
while similar, better designed and solve some issues Pascal had.
Unfortunately, as pascal got adopted by the commercial world, it
started evolving on it's own, leading to the creation of object
pascal as used in Delphi/Lazarus today, and Wirth's other
languages never quite saw the adoption they deserve.

Here's all the post-pascal Wirth languages:

Modula, Modula-2, Oberon, Oberon-2, Oberon-07, Active Oberon,
and the relatively new (not by Wirth) Oberon+

,=================================================,
| Language purity versus practicality & enjoyment |
`================================================='

I like all the aformentioned Wirth languages, but quite fond of
Active Oberon especially. However. I do not write as much code in
these languages as I probably should. The reason being, that there
is very infrastructure around these languages.
Compiler availability is an issue. Especially if you're on an
a-typical platform. This makes your code harder to compile, harder
to package, and ultimately less accessible for an end-user. All
those problems have nothing to do with the language itself, but
are more of a direct result of the language popularity, and yet,
they are problems nontheless. As such, there is a trade-off
between practicality, and language aestethics. The Wirthian
languages are simple, nice and clean, mostly because each of these
languages learns from mistakes of the previous one, and rather
than growing the language organically, an entirely new language
was made. At the very opposite of that, you have C++, with a ton
of organic growth. But because it has remained mostly backwards
compatible with itself, and even C, you have a huge ecosystem,
lots of tooling, lots of use, and a terrible ever growing monster
of a language that keeps getting harder for new users to learn
properly. C++ is the opposite of a nice clean lean language. But
it is also a very practical language. You can get a C++ program
(and a C program as well of course) to compile on most platforms,
even very obscure ones, because the tooling is there.

When I bring up C++, someone usually starts going on about Rust.
I'm not a fan, if simply because of the inferred typing,
abbreviated names ('fn' over 'function', really? why?), it's
tooling and ecosystem (cargo is a blight, for the love of all that
is good, write OS packages, and don't use language-specific
package managers or at the very least make them optional in your
build system instead of mandatory).

Anyhow,... all the opinions above quickly lead to language wars
and endless useless debates. They are useless, because at the end
of the day, a programming language is just that. A language. The
tool you write your computing poetry in. As a computing-poet, I
enjoy a good critical look at what pen I'm going to be writing
with, but while some pens are quantifiably better than others,
people don't typically go and berrate poets for picking an
inferior rickity old pen. It doesn't matter what pen the poet
picks, the poet will still make spelling mistakes. ;) What matters
is, how well can the poet express their art. That brings us to
other reasons why I enjoy smalltalk and Oberon.

If you've made it through my litany of language preferences,
you'll observe that smalltalk actually violates a few of those
preferences, and yet I've always enjoyed working with it. While
smalltalk does violate some of my preferences, it is nontheless,
quite a clean consistent language. However the bulk of my
enjoyment came from my interaction with squeak. The idea of having
a sort-of operating system where everything you use, see, mess
with, is implemented in the language you're writing, and
change-able in real-time is a lot of fun! Similarly, if you've
ever tried A2, the oberon bluebottle OS you will have had a
similar experience. Oberon specifically is quite nice. It has
fantastic demo applications, such as an ssh client, a raytracing
graphics demo, an image effects demo, etc,... and a terminal...
the terminal has, not a bash shell (this is not a unix-like OS
after all), but rather a shell where every command is an oberon
module. Each module's source is on the system you're using and can
be modified. So like with smalltalk squeak, you're in an
environment which you can actively modify on the fly. I find these
things really enjoyable. So at the end of the day, I think
enjoyment trumps most things when it comes to languages. Even if
they might not be entirely practical or check all your boxes.

,==========,
| Hardware |
`=========='

One argument I see pop up a lot in arguments between c/c++ and
rust people is that people say C and C++ are popular for a reason,
which is that they are closer to how the hardware actually
functions. While I prefer C++ over Rust, and am not a fan of Rust
in general, this argument is quite wrong of course. Perhaps an
argument could be made that a pointer, referring to an address in
memory, is a thing a computer understands, except for the fact,
unless you're talking directly to the hardware without an OS, that
memory address is not a real physical address at all. It's a
virtual address that the OS has mapped for your program. And
that virtual address space likely has even more abstractions on
top of it as well. And if you've looked at modern x86-64 asm
instructions, you'll see that there is a lot of fun high-level
stuff, including functions for string handling and what not, that
look nothing like C or C++. Heck, even the instructions we feed
a typical cisc CPU these days might not execute verbatim. There's
an entire black-box voodoo layer of cisc magic that rearranges
things, makes assumptions, and does all kinds of opaque stuff we
have little insight into. It is perhaps true however, that the C
and C++ languages are suitable for the ECOSYSTEM that has grown
out of all of this, that is to say, the combination of your
typical cpu black-box logic and operating system. But of course,
it does not have to be that way.
Things get really fun and interesting if you combine your CPU,
OS, and programming language design as one inter-operating smarly
designed ecosystem. There aren't a lot of examples of that. The
obviously famous example would be the Intel iAPX 432, which wasn't
controlled by assembly language instructions, but instead using
high level languages (Wirthian, incidentially ;) ). I'd also
recommend people take a look at caps-based systems described in
the capa book ( https://homes.cs.washington.edu/~levy/capabook/ )
-- A lot of these systems have failed in the past for various
reasons. But it would be interesting to re-visit some of these
concepts with modern hardware, persistent memory (PMEM), etc,...

,================,
| Sustainability |
`================'

I don't think we should just give up and take the current
computing ecosystem as is. While I prefer C++ to interact with the
typical computing ecosystem in popular use, that doesn't mean I'm
blind to it's myriad flaws. If we had a high-level ISA we wouldn't
have to think about or worry about memory addresses at all. As
such, while it may not be practical in the current world, we ought
to dare re-think computing, and re-think what computing might mean
and look like.

This is especially true in the context of sustainable,
perma-computing. With the planet boiling, we probably ought to
re-think how all the industrialized processes involved in modern
computer manufacturing work. Can we do photolithography in a
sustainable eco-friendly way without harmful chemicals? Can we
build a manufacturing supply chain that doesn't rely on exploiting
the third world, and earth's resources? Can we get by with just
some discrete transistor logic computing? All of these are things
we'll have to figure out sooner or later, and within this is also
perhaps an opportunity to re-imagine the computing landscape. --
Buut that might be the topic of an entirely different phlog post!

-jns