The design of the Mu computer recently got a lot clearer in my head, after I went back and carefully annotated 45k lines of machine code with types. These types are just code comments, of course; I don't get any type-checking in machine code, whether “ahead of time” or at run-time. However, it took me nearly a year of programming to notice that there still is a type system in my head, and it's not quite what I'm used to when programming in Python or even C. Laying it out explicitly has made it a good candidate for Mu's level-2 language, which will be strongly typed and memory-safe, but still mostly map 1:1 to machine code.
In the previous post, I described what my new hobbyist computing stack looks like today, and how the design decisions seemed to accumulate inevitably from a small set of axiomatic goals. In this post I describe some (far more speculative) future plans for Mu and try to articulate how the design process continues to seem pre-ordained.
(Many of the sections below outline constraints before describing a design that mostly fits them. This flow is inspired by Christopher Alexander's “Notes on the synthesis of form”.)
— Richard Gabriel's best summary (pg 219) of his essay, “Worse is Better”
Over the past year I've been working on a minimal-dependency hobbyist computing stack (everything above the processor) called Mu. The goal is to:
- build up infrastructure people can enjoy programming on,
- using as little code as possible, so that people can also hack on the underpinnings, modifying them to suit diverse desires.
— Ian Malcolm, “Jurassic Park”
— Christopher Alexander, “A Timeless Way of Building”
Lately I tend to program in a certain unconventional manner. A series of design choices, each seemingly reasonable in isolation, take me pretty far from conventional wisdom.
Most programmers agree that we don't read enough code. The interviews in Peter Seibel's book, “Coders at work” highlight a comical contradiction: almost all the programmers interviewed by Seibel recommend that others read code for fun, but none of them routinely do so themselves. Seibel even asked Hal Abelson (of SICP fame) directly about this phenomenon:
I've been periodically wrestling with the concept of continuations for several years now, and have somehow never gotten comfortable with them. Looking back, I think this was for two reasons:
- Continuations are usually explained in the context of high-level languages with higher-order functions and lots of nested function calls. But continuations fundamentally subvert the notion of “function”. Operators like ‘reset’ looked like functions, but had “spooky action at a distance” effects that were hard to reason about.
- I had trouble finding real-world programs where continuations are more
expressive than regular recursive function calls with well-designed data
structures. For example, classic back-tracking problems like the
N-queens problem have elegant solutions that don't require continuations.
Were continuations just a low-level primitive for building more high-level
tools like generators (‘yield’) and exceptions? Building
fluency with a concept requires developing an instinct for when it's applicable.
[Update Nov 27: This post had issues, and I retract some of my more provocative claims. See the errata at the end.]
All software comes with a version, some sequence of digits, periods and characters that seems to march ever upward. Rarely are the optimistically increasing versions accompanied by a commensurate increase in robustness. Instead, upgrading to new versions often causes regressions, and the stream of versions ends up spawning an extensive grapevine to disseminate information about the best version to use. Unsatisfying as this state of affairs is to everyone, I didn't think that the problem lay with these version numbers themselves. They're just names, right? However, over the past year I've finally had my attention focused on them, thanks to two people:
(Or why goto is worth keeping around in modern languages.)
A cool thing happened during a lesson today, and I wanted to try to capture the magic before it slipped through my fingers. It happened while I was trying to teach recursion (without ever using that word) using my side project, Mu. The experience got me thinking about the quote above, and wondering if there was a way to bridge the summits of C and Lisp without having to go through the “swampy lowlands” between them.
It's a common trope among programmers that a single computer contains enough bits that the number of states it can be in far exceeds the number of atoms in the universe. See, for example, this 3-minute segment from a very entertaining talk by Joe Armstrong, the creator of the Erlang programming language. Even if you focus on a single tiny program, say one that compiles down to a 1KB binary, it's one of 21024 possible programs of the same length. And 1KB is nothing these days; larger programs get exponentially larger spaces of possibility.
The conventional response to this explosion of possibilities is to observe that the possibilities stem from a lack of structure. 10 bits encode 210 possibilities, but if you divide them up into two sub-systems with 5 bits each and test each independently, you then only have to deal with twice 25 possibilities — a far smaller number. From here stem our conventional dictums to manage complexity by dividing systems up into modules, encapsulating internal details so modules can't poke inside each other, designing their interfaces to minimize the number of interactions between modules, avoiding state within modules, etc. Unfortunately, it's devilishly difficult to entirely hide state within an encapsulated module so that other modules can be truly oblivious to it. There seems the very definite possibility that the sorts of programs we humans need to help with our lives on this planet intrinsically require state.
So much for the conventional worldview. I'd like to jump off in a different direction from the phenomenon of state-space explosion in my first paragraph. Read more →
I've been teaching programming using my new UI for just about six weeks now, and it's been miraculous and endlessly fascinating to watch a young mind up close. I'm still processing many of my observations and lessons, but I want to describe how one of my students learned something I never meant to teach: consistent indentation.
The only thing I did to help him learn indentation was this: I never ever brought it up. Mostly because I just don't consider it very important, especially at such an early stage. When I wrote programs to demonstrate features I indented as I normally would, then I'd hand over the keyboard, and ignore the fact that my student was abutting each line of code with the left margin.