If you ever talk to, or read an interview with, a musician, they will inevitably, and quickly, end up talking about their influences. While there is an element of nostalgia to this, perhaps even of acknowledging debts, I see its main purpose as helping spread knowledge about who the most interesting set of musicians are.
In programming, in contrast, we rarely talk about our influences, other than a frequently expressed allegiance to a single programming language. This seems a shame to me, because it denies new people to our field helpful pointers to programmers and systems whose style might be a useful influence.
As a modest attempt to rectify this situation, I’m going to show how one particular system, OpenBSD, has had a big influence on my programming style over time. It’s far from the only system that’s influenced me – not to mention various programmers and communities who’ve also been an influence [1] – but I need something concrete to use as an example.
After a long period as a “normal” user, over time I found myself diving more often into OpenBSD’s code. Mostly, I was simply trying to understand how something worked, though occasionally I was trying to fix something [2]. However, the C code that makes up the bulk of OpenBSD seemed alien to me, because it was incredibly terse, and I often gave up trying to understand it rather quickly. In contrast, the programming style that I was using was very verbose. I used long variable names (on the basis that they helped readers understand what was going on), lots of whitespace (on the basis that it helped readers understand what parts of a file were most related to each other), and wasn’t afraid to duplicate code with minor variations (on the basis that each variation was then clearer to read).
You can perhaps imagine how surprised I was when, for example, I looked at
the source code for OpenBSD’s echo
command [3]:
int main(int argc, char *argv[]) { int nflag; /* This utility may NOT do getopt(3) option parsing. */ if (*++argv && !strcmp(*argv, "-n")) { ++argv; nflag = 1; } else nflag = 0; while (*argv) { (void)fputs(*argv, stdout); if (*++argv) putchar(' '); } if (!nflag) putchar('\n'); return 0; }
It boggled my mind that a useful command could require so little code. If I’d
written that, it would have required at least 2, probably 3 times as much code
[4]. What was my conclusion? Well, of course, being a
normal human being, I knew that the OpenBSD version of echo was unreadable (I
mean, what human can interpret *++argv
?!), and that my
(hypothetical) longer version would have been much better. So I kept on writing
code in my verbose style.
Several years later I came to write extsmail, a Unix email sending daemon — the first time that I’d tried to write such a program. I spent quite a while looking through code in the OpenBSD source tree to try and understand how such a daemon might work. Gradually I started to realise that not only could I cope with OpenBSD’s terse code style, but it was actually easier for me to read it than code I’d written myself. This was, to put it mildly, surprising: I thought my programming style made life easier for readers but the polar opposite style seemed to be better.
At the time I couldn’t articulate why OpenBSD’s style was better,
partly because my ego didn’t really want to let go of the idea that
verbose code was better. I now believe the reason is fairly simple: in essence, OpenBSD’s
style squeezes more code onto the screen. It makes local chunks of code (e.g.
for
loops) a bit harder to read but gives the reader a better
sense of the wider context (e.g. a function or set of related functions). This,
I believe, is a good trade-off because, in programming, the wider the context
one is considering, the harder it is to gain a good understanding of that
context. My style made understanding local context easier but
made it noticeably harder to understand wider contexts.
Even before I could articulate the reasons why OpenBSD’s style was better, I started to adjust my programming style. It might not be obvious if you look at parts of extsmail’s source code, but I started trimming down variable names and whitespace, and I became more comfortable using what I’d once thought of as “weird” C idioms. Each time I went a little further in that direction, I found my code a little easier to read, especially if there was a gap in time between writing and reading that code.
Over time I realised that a key aspect of this programming style is rewriting. I start by trying to make something which works then I try and make it smaller. By the time I’d come to write hk (the first new C program I’d written in quite some time), I think my evolution was clear. hk’s entire source code is currently a paltry 224 lines. If you compare this chunk of code:
unsigned int ignorable_modifiers(Display *dpy) { KeyCode ignorable_keycodes[sizeof(IGNORABLE_MODIFIER_KEYSYMS) / sizeof(KeySym)]; for (size_t i = 0; i < sizeof(IGNORABLE_MODIFIER_KEYSYMS) / sizeof(KeySym); i++) { ignorable_keycodes[i] = XKeysymToKeycode(dpy, IGNORABLE_MODIFIER_KEYSYMS[i]); } XModifierKeymap *mm = XGetModifierMapping(dpy); unsigned int mask = 0; for (int i = 0; i < 8 * mm->max_keypermod; i++) { for (size_t j = 0; j < sizeof(ignorable_keycodes) / sizeof(KeyCode); j++) { if (ignorable_keycodes[j] != NoSymbol && mm->modifiermap[i] == ignorable_keycodes[j]) { mask |= MODIFIER_MASKS[i / mm->max_keypermod]; } } } XFreeModifiermap(mm); return mask; } int main(...) { ... unsigned int ignore_mask = ignorable_modifiers(dpy); unsigned int mask; KeyCode keycode; parse(dpy, argv[0], &mask, &keycode); XGrabKey(dpy, keycode, mask, root, False, GrabModeAsync, GrabModeAsync); for (unsigned int i = 0; i <= ignore_mask; i++) { if ((ignore_mask & i) == i) { XGrabKey(dpy, keycode, mask | i, root, False, GrabModeAsync, GrabModeAsync); } } ... }
to the approximate equivalent in xbindkeys you’ll notice a significant difference in approach. My first attempt at the above looked a lot like xbindkeys: I had to keep rewriting in order to reduce the code until I felt I’d made it sufficiently small while still (hopefully!) being correct and readable [5].
Wrapping up
The point of this post isn’t try and persuade you that you should also use OpenBSD’s style as an influence, but I wanted to show what the effects of a programming style influence can be. I could equally have talked about how BBC BASIC, Arm 2 assembly, Python, Template Haskell, or many other languages and systems have influenced me, and I think the overall thrust of this post would have been similar.
In my opinion, any given influence can, or at least should, only take you so far: there will always be differences in your context from that of the influence itself. For example, I am much more fond of long comments than OpenBSD is [6] because I find that helps me, and I think others, reason about the code’s correctness more thoroughly. In a very different manner, and heavily influenced by my time working in RPython, I have been persuaded of the virtues of having extensive test suites.
An interesting question is when one is in the right place to accept and make use of any given influence: any given influence necessarily comes with a context where that influence makes sense. For example, when I was more of a beginner, I don’t think OpenBSD’s style could have influenced me: its costs would have been obvious to me, but I doubt I’d have been able to appreciate its benefits.
One good thing about growing older is that I add to my set of influences far more often than I remove something. In fact, I’m not even sure if I’ve ever removed an influence, though some have been wholly superseded by others [7]. These days most of my programming is in Rust. OpenBSD’s C style still influences my Rust style, because the core lessons I took from it apply to any other language. However, new influences have made their impression upon me: for example, I’ve been heavily influenced by some of the functional programming idioms surrounding iteration that Rust encourages.
One implicit assumption amongst musicians is that there’s nothing wrong with singing the praises of those who influenced you. It doesn’t make the musician in question an unquestioning “fanboy” or diminish the novel aspects of their music. It would be nice if we could do something similar in programming. Personally, I’ve been lucky to be influenced by some great software and I hope that my influences will continue to evolve!
Acknowledgements: thanks to Edd Barrett, Lukas Diekmann, Dan Luu, and Davin McCall for comments.
Footnotes
It’s worth noting explicitly that that beyond those I’m consciously aware of, other programmers and systems will have influenced me, in particular, but not only, when that influence is via a third party.
It’s worth noting explicitly that that beyond those I’m consciously aware of, other programmers and systems will have influenced me, in particular, but not only, when that influence is via a third party.
Over the years I’ve had a small number of small patches included in OpenBSD. My contributions are insignificant, but it’s been my small attempt to pay back a bit of the debt I owe to this wonderful piece of software.
Over the years I’ve had a small number of small patches included in OpenBSD. My contributions are insignificant, but it’s been my small attempt to pay back a bit of the debt I owe to this wonderful piece of software.
The version I’m quoting is from around the time I’d have first seen the source
code. Since then echo
has bloated significantly with two extra lines to add pledge
support!
The version I’m quoting is from around the time I’d have first seen the source
code. Since then echo
has bloated significantly with two extra lines to add pledge
support!
Though I doubt I’d have managed to make it quite as long as GNU’s version of echo, even if I had implemented the same number of features.
Though I doubt I’d have managed to make it quite as long as GNU’s version of echo, even if I had implemented the same number of features.
If you’re wondering why there are some very long variable names in this chunk of code, it’s because I kept getting confused by X11’s separation of key symbols and key codes. In fact, looking at the code now, I’ve again forgotten which is which!
If you’re wondering why there are some very long variable names in this chunk of code, it’s because I kept getting confused by X11’s separation of key symbols and key codes. In fact, looking at the code now, I’ve again forgotten which is which!
As a couple of random examples see this file from snare or this file from grmtools.
As a couple of random examples see this file from snare or this file from grmtools.
For example, in my teens I used a language, whose name I can no longer even recall, created by someone on the bulletin board I used. It was, roughly speaking, a mix of Pascal and C. I learnt quite a bit from that, although I think I (re)derived the same lessons when I got further into C in later years.
For example, in my teens I used a language, whose name I can no longer even recall, created by someone on the bulletin board I used. It was, roughly speaking, a mix of Pascal and C. I learnt quite a bit from that, although I think I (re)derived the same lessons when I got further into C in later years.