Tuesday, August 20, 2024

Objective-C is just, like, a leaky abstraction over C

Recently I came across this 10 year old post by Robert Atkins: Objective C is like Jimi Hendrix. It is about reconciling admiration for Objective-C by the "old-timers" with the newcomers' somewhat less enthusiastic response.

His very cogent insight was that Objective-C, like Jimi Hendrix, introduced new concepts that were somewhat revolutionary (""mind blowing") at the time, but are now taken for granted:

So if you'e new to Objective-C and, as I am, struggling to come to terms with the fact that it's one great big leaky abstraction on top of C, put yourself in the shoes of an 80s C programmer and remember you get to use these neat "modern" features in a systems programming language.
As one of the very early, pre-NeXT, adopters of Objective-C, I have a slightly different take: That implicit "despite" is actually very much a "because" for me.

The modern features in Objective-C such as a dynamic messaging, a runtime with introspection and intercession etc. were not new at the time, and they were not really "mind-blowing". LISP and Smalltalk had had them for a long time. But so far having these features had required large, complex runtime environments that were very distant from the rest of the machine, and usually quite isolated from the rest of the machine. Still are, to this day. You don't program your computer with LISP or Smalltalk. Your computer runs a separate LISP or Smalltalk computer that you can then program on its own terms and in its own world. (And attempts to bring machines closer to these languages mostly did not succeed).

Objective-C provided these features with the slimmest of a sliver of an extension to a portable PDP-11 macro assembler.

Now that was mind-blowing!

And it goes beyond that: there actually was, at some point, Objective-Assembler. Objective-C was never intended to be "a language" like Java or Rust. The Objective part of Objective-C is a glue layer, an integration mechanism that can be added to any language. There was Objective-FORTRAN, Objective-Pascal etc. Helge Hess once put it almost perfectly: Objective-C is COM with language support. Or SOM. Or whatever interop mechanism they come up with again (Swift "library evolution", I am looking at you, can we have Objective-Swift?).

So it wasn't just mind-blowing, it also was, is, and remains incredibly useful.

So useful, in fact, that this slimmest sliver of an extension gradually replaced the portable PDP-11 macro assembler it was sitting on top of for most day-to-day use. So much so, that in the Apple developer ecosystem, the much larger C part started to be regarded as a completely separate language that most devs never dared touch.

Of course the fact that this is doable shouldn't be surprising, after all the Objective part is modeled after Smalltalk, and Smalltalk is a complete programming language. But Smalltalk requires a fairly large VM to run, typically coded in C or in Squeak's case, C plus BCPL-encoded-in-Smalltalk that gets automatically translated to C. Objective-C and Objective-Assembler showed that you don't actually need all that, just a tiny messaging function on top of bare machine is not just sufficient, it's also faster, integrates better and is easier to implement. Less is indeed more.

All that doesn't mean that Objective-C isn't an ad-hoc car crash of two languages with overlapping functionality and syntax, and the safety of C's memory model and Smalltalk's type system. That this disaster area works better in practice than most other languages tells you all you need to know about the state of PL design.

A more principled, if irreverent, exploration of the same approach can be found in Ian Piumarta's COLA (COmbined Lambda Architecture). He basically discovered the same principles, and came up with something that's even cooler, though at this point less practical.

My favorite bit (it's a tight field) is how he manages to build a message-dispatcher that is itself invoked by message-send. How does he resolve the infinite recursion inherent in the self-referential definition? By pre-populating a single cache.

Ian is also a really great speaker:

Alas this video of his Stanford EE380 talk is only 240p, so the text is illegible, but fortunately there are slides that you can follow along.

In Objective-S, the procedural/OO part is really based on just that sliver of an extension. The C part is removed (yes, a real "Objective-C without the C"), replaced by a few type declarations for indicating primitive types, special stores for raw memory access when needed and a generalization of message-sending that subsumes calling C functions.