Over the course of 2022, I've found myself gradually programming in a certain
way that has been working really well. Here, let me show you a few examples,
see if you can spot the pattern:
Minimal dependencies, easy to build, runs anywhere you can install apps
without asking permission, thoroughly tested, designed above all to reward
curiosity about its internals.
The Pong fork.
Baking the editing environment into other apps for a self-contained curiosity-rewarding/forking
experience.
(Notice the extensible graphical logs. It takes very little code to augment
debug-by-print. This work stemmed from the Handmade
Network Wheel Reinvention Jam)
Using the editing environment to debug the editing environment. (More
tools should support a command palette; it's the best of commandline and GUI
worlds.)
LuaML, a box model
over an infinite 2D surface that you can pan and zoom without restriction.
Built in the live style, of course.
(You can edit each text widget, and scrolling within a widget pans the whole
surface. This took me a couple of tries to boil down to a reasonably elegant
implementation.)
I've found myself calling these freewheeling apps to myself. They're
freewheeling in two ways. First, they're easy to get started with so you can
be off doing your thing. Second, they stay freewheeling over time. They don't
cramp your style with constraints after
you've gotten suckered into adopting them.
“You want me to trust your binaries? I'm just not ready for that
commitment man. Do you have a rock-solid build process that's guaranteed to
work on my machine? I'd like the option to look at what you're up to
when I feel like it.”
“It autoupdates twice a week? I'd rather not spend time tracking down
what change broke my habits, thanks.”
“1GB install? What if I'm in, you know, the other 95% of the
planet?”
“You have a PR submission process? How lovely for you. Hey, how about I
just publish a fork, and you take what you want. (I love getting comments,
though.)”
“I don't want to remember a bunch of idiosyncracies about your language
and app. Can you just give me good error messages when I mess up? Just don't
harsh my buzz about portability, compatibility constraints and whatnot.”
Replace “I” with yourself, dear reader. These apps should work
anywhere (except mobile platforms), be easy to try out, easy to edit in place,
and easy to subvert if you dislike a design choice. I'm going to continue
improving the hacking experience, and I want to support forks in staying up to
date with my changes. Unfortunately they require a little bit of programming
experience for now (particularly git), but it should all seem pretty
familiar regardless of what languages and tools you've used in the past. And
if they don't, feel free to reach out. I welcome questions.
Coda
A lot of the bang here comes from the stack I'm using: the Lua
programming language and the LÖVE game engine
for Lua. You don't have to use Lua and LÖVE to be freewheeling, I
think, any parsimonious stack designed to be portable and easy to build will
do. But if you have another candidate that meets those criteria, I'd like to
see it.
I spent the pandemic year reading a lot of Peter Hamilton. I wouldn't necessarily
recommend it; they all blur together after a while, and I start to wonder if
they aren't perhaps all the same story…
Regardless, the first Peter Hamilton I read, Pandora's Star, still
sticks with me for a motif that didn't come together until right at the end:
the Silfen Paths. In this universe humanity has portals that can span light
years, often conveying train service between star systems, but there are
occasional legends of an older interstellar network by an ancient alien
civilization. Needless to say, our intrepid protagonist manages to get on this
network. And suffers years of privation and amazing adventures (while everyone
else in the novel is moving the story forward) before coming out the other
end. Unlike the portals created by humans, the Silfen paths don't contain
abrupt transitions between two points in space. Things blend together more
gradually. Also unlike portals, the Silfen Paths aren't in the traveller's
control. Instead, to go forth on the paths is to open oneself to the new, the
unexpected. Extreme heat and cold. Danger. The occasional prancing Silfen
who'll happen upon you and help you out, but who doesn't quite seem to get the
idea of “home,” or that you're trying to get there, before
outpacing you again, inevitably leaving you behind to find your own path
through the maze.
My goal for Mu is
software that is accountable to the people it affects. But it's been difficult
to talk to people about Mu's goals because of the sheer number of projects
that use similar words but lead to very different priorities and actions. Some
of these I like to be associated with, some not so
much.
“There are two ways of constructing
software. One way is to make it so simple that there are obviously no deficiencies,
and the other way is to make it so complicated that there are no obvious
deficiencies. The first method requires a willingness to accept limitations,
and to compromise when conflicting objectives cannot be met.” — C.
A. R. Hoare
It seems to me that modern computers trap people in a vicious cycle. Compatibility
guarantees breed complexity over time as the world changes. Complexity is
managed by introducing layers of abstraction. Abstractions introduce new
compatibility guarantees. Over the decades this vicious cycle leads to even
professional programmers understanding only a tiny fraction of the software
infrastructure that runs their computers. As a result, our world is increasingly
captured by software that is unaccountable to people.
For several years now I've had a vision for a computer
that allows anyone to audit its inner workings, where any operation can be
decomposed strictly into a parsimonious combination of simpler operations,
terminating without cyclic dependencies or circular reasoning at some ground
level. Ideally it would do this in a way that rewards curiosity, leading to a
virtuous cycle where an order of magnitude more people grow to understand how
their computer works as they use it.
Nowhere in this picture are compatibility guarantees, version numbers or
forced upgrades. At any point your computer should be internally consistent
and free of known historical accidents. Even if this means upgrades are more
work and so more infrequent, and that our computers must be slower. Or do
less. That seems like a worthwhile trade for a
more sustainable world.
At the start of 2020 the state of the
Mu computer looked like this:
Over the last few months I've written up in one place the entire argument
for—and comprehensive description of—what I've been working on
since 2014. It will be published in the proceedings of the Convivial
Computing Salon. From the
call for submissions:
In Tools for Conviviality [1973], Ivan Illich said, “I choose
the term ‘conviviality’ to designate the opposite of industrial
productivity… Tools foster conviviality to the extent to which they can
be easily used, by anybody, as often or as seldom as desired, for the accomplishment
of a purpose chosen by the user… People need new tools to work with
rather than tools that work ‘for’ them.”
We were promised bicycles for the mind, but we got aircraft carriers instead.
We believe Illich’s critique of the damage to society from technology
escalation offers a fresh perspective from which to discuss the pathologies of
modern software development, and to seek better alternatives.
An inspiring theme. My response: “Bicycles
for the mind have to be see-through.” Get it? When I look over at my
bicycle I can see right through its frame. I can take in at a glance how the
mechanism works, how the pedals connect up with the wheels, and how the wheels
connect up with the brakes. And yet, when we try to build bicycles for the
mind, we resort to “hiding” and “abstraction”. I think
this analogy has a lot more power than we credit, a lot more wisdom to impart
if we only let it in. I think conviviality requires tools with exposed
mechanisms that reward curiosity.
I've been trying to falsify this hypothesis for 6 years. There are still large
gaps to investigate, but so far it's holding up. Read
on → [pdf; 25 pages]
Mu: Sketching out a minimal system programming language
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.
“It is far better to have an
under-featured product that is rock solid, fast, and small than one that
covers what an expert would consider the complete requirements.” — 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.
“Most kinds of power require a
substantial sacrifice… By the time someone has acquired it, he has also
matured to the point where he won't use it unwisely.”
— Ian Malcolm, “Jurassic Park”
“It is impossible to form
anything which has the character of nature by adding pre-formed parts.”
— 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 want to dig a little deeper on
this. You, like many other people, say programmers should read code. Yet when
I ask what code have you read for fun or edification, you—also like many other
people—answer that you read students’ code, which is your job, and review code
at Google, which is also your job. But it doesn’t sound like you sit down of
an evening with a nice printout and read it.
Delimited continuations in a statement-oriented language
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.