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