Monday, February 17, 2025

Transcendental Computer Languages: Or How I Learned to Stop Worrying and Embrace the Lisp

When it comes to computer languages, there is a mantra popular among software engineers.  "Use the right tool for the job!"  For every project you might want to work on, there's a computer language just right for that project -- what's more, for every aspect of that project, there's the perfect language!  Every software developer should have a dozen or two languages in their toolbox, ready to go, to do do everything effortlessly using the best tool possible.

While computer languages seem to "specialize" for different "use cases" -- Assembly language is a language unique to a given processor; a Systems language is "close to the metal", requiring you to do "bookkeeping" chores by hand, but at least gives you "if/else" branches, "for" loops, and "functions"; and from there, higher level languages that manage memory for you, and have a lot of features built in or easily available as libraries.  If you're doing something that's "throw away after using once" or is so dominated by waiting for user input and internet communication times that the processor is mostly idle, you don't really care if you stop to automatically every fem moments to clear out some memory -- but if you're working at the inner core of the operating system, you'll want to use a Systems language where every bit of memory is managed by hand, and every moment is timed perfectly ... and where you can "dip into" Assembler if you're doing something particularly esoteric.  This naturally creates a hierarchy of languages, from "low-level" to "high-level", with the most manual at the bottom, and the most hand-holdy at the top.

So you wouldn't use a Systems language when writing an operating system, right?  And you wouldn't use a high-level language when working on the operating system kernel.  And you'd want to use a language specializing in web development for creating and managing websites, or a data processing language to do statistics and analysis, or a UI language for designing user interfaces, or if you want to, well, ....

Yeah, about that ... for all this talk about "The right tool, darn it!" there's an awful lot of "settling" into just a handful of tools.  C for systems programming, PHP and HTML/CSS/JavaScript for web programming, MySQL for databases (put aside that PostgreSQL may be a bit better), Python for data crunching, C++ for some applications ... and I'm not at all convinced that some of these are really the "right tools".  JavaScript, in particular, dominates its space not because it's the world's greatest language for capturing interface design and web interaction, but because it's pretty much the only option offered it its space.  C and C++ dominate where they do mostly because of a mix of inertia and Tradition, tradition!, particularly with languages like Rust on the horizon, promising to offer systems programming with the safety of scripted languages, somewhat.

The funny thing about most of those languages, is that most of them are "Turing complete" -- which is a fancy way of saying that every language can do anything any other language can do -- and because it can take so much time to master a language, and that language environment becomes a comfortable place to live, and it's such a hassle to download and install a new language ... all too often, the "best tool for the job" is simply "the one the project's already written in" or "the one I'm most comfortable with" (or, in the case of companies, "the three or four languages we think most of our software engineers are comfortable with") ... and because many languages are "general purpose", they can do everything you need to do, even if some of what you do is "against the grain" of the language itself.

Yet, despite this, there are all sorts of reasons for learning new languages!  Many years ago, when I was starting out as a web developer and software engineer, I stumbled onto a weird "functional" language called Haskell.  At the time, my brother-in-law asked me "Why are you learning that?  Where are you going to use it?", and I don't think I answered the question well at the time (or even perhaps at all), nor do I think I can coherently answer it right now.  As vague as I have to be about the answer, however, I can at least provide a few suggestions:  it's a new paradigm that, if I understand it, can help me be a better programmer; it's something new and interesting, and I crave variety, because one can't live life solely dedicated to only things that are "useful" (although I probably crave variety more than most people); as a mathematician, I couldn't help but admire its pure mathematics roots (yet, as an exploratory software engineer, I simultaneously despise its pure math roots, figuring one shouldn't have a PhD in mathematics to comfortable programming!); it's weird, and I love weird things; who knows, maybe it will prove to be useful after all?

And besides, eventually, it did prove useful!  I spent a few months using Haskell to help implement a "consensus" algorithm for a cryptocurrency ledger.  Also, several years ago, I started seeing weird themes crop up in JavaScript meetups:  algebraic types -- for better and for worse, Haskell-ish things were making their way to the web!

But those use cases were well into the future.  In the meantime, as I was first learning Haskell, I gradually developed the notion of a "transcendental" language -- a language that could reach down into the lower levels of the system, and eek out performance there, yet stretch out to the highest levels of abstraction, and provide simplicity without worrying about all those "fiddly bits" that makes low-level programming such a pain.  Ironically, I would shortly conclude that Haskell isn't quite transcendental, as powerful as it may be:  it's too rigid, it requires too much understanding of higher mathematical ideas, and it's too "bulky" (someone once pointed out to me that the compiler is about a gigabyte in size) -- to really be "transcendental".

But my exposure to Haskell introduced me to a language that is transcendental.  While Haskell is the first pure functional language, the first functional language came out in 1958, "specializing" in "LISt Processing", created a year after a language specializing in "FORmula TRANslation" came on the scene.  Because Haskell is a relative newcomer compared to these languages, when Haskell enthusiasts extolled the virtues of "functional programming", they relied heavily on essays describing the power of LISP.  And thus, I started down the path of learning about Common Lisp and Scheme.

(I had technically encountered LISP before, in a book comparing four different languages, but the book was so focused on showing how you could do the same things in four vastly different languages, it somehow made the languages seem equally capable, so at the time, I wasn't convinced I ought to explore LISP.  Having said that, it's been years, and I may be misremembering -- or may have misunderstood -- the tone of the book.)

Unlike "FORTRAN", which really was a language that specialized in formula translation (and to the degree it departs from this, there's the saying that "No one knows what language scientists and engineers will use 100 years from now, we just know it's going to be called FORTRAN"), LISP didn't exactly specialize at all.  LISP introduced pretty much everything we expect in a modern language -- functions as first class citizens, garbage collection, symbols, variable and keyword arguments to functions, anonymous functions, native data structures and the means to create new ones -- well, maybe not everything we expect -- things like object-oriented programming and exception handling didn't come until later -- but it managed to literally absorb those things as they came down the pike -- and LISP includes, even now, a few things we don't have in computer languages, such as macros written in the language itself -- taking advantage of the fact that every program written in LISP is a list of lists, which are essentially trees, which are easy-to-manipulate data structures -- and thus, it's possible, even easy (or at least, easy-ish, because some problems are inherently hard) to create "mini languages" that are just as much a part of the language itself, as any other thing you might do for that language.

Thus, LISP has this ability to reach into the very bowels of a system, even resorting to Assembler if it has to, yet it can easily reach the highest heights of abstraction.  LISP is a transcendental language!

LISP isn't the only one, to be sure.  I wondered whether FORTH should be considered one, because it's low-level and doesn't have garbage collection, but a demo of FORTH on an Arduino with 2 kilobytes of memory convinced me it deserves to be included in the category.  I also figure Smalltalk, too, should be included, although it's not well known as something that can reach down to lower levels -- but it's not hard to see how that ability could be added to a flavor of Smalltalk, if it is needed.  Are there others?  I don't know.  I do know, however, that some of my favorite languages -- APL and J, in particular, and Elixir as well -- have ideas I appreciate, but nonetheless don't manage to transcend the differences between different levels of language.  (And for Elixir, I wonder if I'd change my mind if I understood the language better.)

It is interesting to observe that these three transcendental languages have been used to write operating systems -- this is only natural, considering their ability to reach down into the bowels of things -- and these operating systems are known for their flexibility, which is the result of their ability to abstract ideas.  This doesn't necessarily mean that only transcendental languages can be used to write an operating system -- BCPL, C, C++, PL/1, and even Pascal and FORTRAN are low-level counter-examples, and I'd suggest that Python and Ruby, while not really operating systems, can be counter-examples as well -- but overall, if a language truly is transcendental, it's going to have a stronger "gravity" towards becoming an operating system than other computer languages.

But then, this is likely a red herring, anyway.  At some point, I think I'll write a post explaining why all languages want to be operating systems!

No comments:

Post a Comment