I’ve been struggling for at least a year to come up with a way to talk about this that makes some sort of sense, since I would like to help people start somewhere in understanding what it means to get on the track of “computer science as an aspirational goal.” For me, the first steps have been to understand that some tools have been invented that can be used in this work, and to work on a conceptual model of a processor. Part of that is beginning to understand what constitutes a processor, and so it’s important to do some study of processors, even if it’s just at a conceptual level. That doesn’t mean you have to start out understanding the electronics, though that can also be interesting. It could be (as has been the case with me) looking at the logic that supports the machine instructions you use with the processor.
The existing tools are ones you’ve heard about, if you’ve spent any time looking at what makes up traditional computing systems, and what developers use: operating systems, compilers, interpreters, text editors, debuggers, and probably processor/logic board emulators. These get you up to a level where, rather than focusing on transistor logic, or machine code, you can think about building processors out of other conceptual processors, with the idea being that you can build something that carries out processes that map to clear, consistent, formal meanings in your mind.
What started me on this was I referred back to a brief discussion I had with Alan Kay on what his conception of programming was, back in the late 2000s. His basic response to my question was, “Well, I don’t think I have a real conception of programming”. In part of this same response he said,
Smalltalk partly came from “20 examples that had to be done much better”, and “take the most difficult thing you will have to do, do it, and then build everything else out of that architecture”.
He emphasized the importance of architecture, saying that “most of programming is really architectural design.”
It took me several years to understand what he meant by that, but once I did, I wanted to start on figuring out how to do it.
A suggestion he made was to work on something big. He suggested taking a look at an operating system, or a programming language, or a word processor, and then work on trying to find a better architecture for doing what the original thing did.
I got started on this about 5 years ago, as I remember. As I look back on it, the process of doing this reminds me a lot of what Kay said at the end of the video I talked about in Alan Kay: Rethinking CS education.
He talked about a process that looks like a straight line, at first, but you run into some obstacles. In my experience, these obstacles are your own ignorance. The more you explore, the more you discover some interesting questions about your own thinking on it, and some other goals that might be of value, which take you some distance away from the original goal you had, but you feel motivated to explore. You make some progress on those, more than what you started out trying to do, but still run into other obstacles, again from your own ignorance, which, when you explore some more, you find other goals that again, take you farther away from your goal, and you make even more progress on that. If you think on it, everything you’re doing is somehow related to the goal, but it can give you some concern, because you may think that you’re wasting time on trivial goals, rather than the goal you had. The way I’ve thought about this is like “Backing up from the problem I thought I was trying to solve,” looking at more basic problems. It seems to me the “terrain” that Kay was showing in the virtual map was really the contours of one’s own ignorance.
Describing how I got into this (you don’t have to take this path), I looked at a very simple operating system, from a computer I used when I was a teenager. I looked at a disassembly of its memory, since it was built into ROM. The first realization from looking at the code was to see that an operating system really is just a program, or a program with an attached library of code that the hardware, and user software often jumps into temporarily to accomplish standard tasks. The other realization was answering the question, “How does this program get started?” (It turns out microprocessors have a pre-programmed boot address they go to in the computer’s address space, which is set up as boot code in ROM, where they begin fetching and executing instructions, once you power on the processor.) In modern computers, the boot ROM is what’s traditionally called a BIOS (Basic Input/Output System), but in the computer I was studying, it was the OS.
The basic outline of my project was to study this OS, and try to see if I could improve on its architecture, reimplement it in the new architecture, and then “make everything else” out of that architecture.
I quickly realized a couple things. The first was that I didn’t want to be dealing just in assembly language for the whole project. The second was a sneaking feeling I’ve learned to listen to in my learning process that (in this case) I was ignorant of the underlying processor architecture, and I needed to learn that. My answer to both of these problems is that I need to model some higher level process concepts (to get out of working in assembly, yet still use it to generate a new implementation out of a higher level model–a compiler), and I need to construct a conceptual model of the CPU, so I can gain some understanding of the machine the OS is running on. In my learning process, I’ve found it most helpful to understand what’s going on underneath what I’m using to get things done, because it actually does matter for solving problems.
I needed a tool for both creating a programming model (for my higher-level process concepts), and for modeling the CPU. I chose Lisp. It felt like a natural candidate for this, though this is not a prescription for what you should use. Do some exploration, and make your own choices.
I started out using an old dialect of Lisp that was watered down to run on the old computer I was studying. I had a goal with it that I’ve since learned was not a good idea: I wanted to see if I could use the old computer to write its own OS, using this method. What I’ve since learned through some historical research was when Kay was talking about what they did at Xerox Parc, they definitely didn’t use this method I’m describing, anything but! They used more powerful hardware to model the computer they were building, to write its system software. Nevertheless, I stuck with the setup I had, because I was not ready to do this. I found a different benefit from using it, though: The Lisp interpreter I’ve been using starts out with so little, that in order to get to some basic constructs I wanted, I had to write them myself. I found that I learned Lisp better this way than using a modern Lisp environment, because in that, I kept being tempted to learn to use what was already available in it, rather than “learn by doing.”
I had to step back from my goal, again, to learn some basics of how to gain power out of Lisp. I have not regretted this decision. In the process, I’ve been learning about how to improve on the Lisp environment I’ve been using. So, it’s become a multi-stage process. I’ve put the idea of improving the OS on hold, and focused on how I can improve the Lisp environment, to make it into something that’s powerful for me, in a way that I can both understand, and in a way that serves the larger goal I have with understanding the target system.
As I’ve worked with this underpowered setup, I’ve been getting indications now and then that I’m going to need to move up to a more powerful programming environment (probably a different Lisp), because I’m inevitably going to outgrow the current implementation’s limitations.
The deviations I’m describing here are about first pursuing a goal as a motivator for taking some action, but realizing that I had more to learn about the means of achieving the goal, putting the initial goal on hold, and spending time learning the means. It’s a process of motivated exploration. It can be a challenge at times to keep my eye on the initial goal that got me started on this whole thing, which I still have, because I get so focused on learning basic stuff. I just have to remind myself from time to time. It’s like wanting to climb Everest when you have no experience climbing mountains. There are some basic skills you have to develop, some basic equipment you have to get, and physical conditioning you have to do first. Just focusing on that can seem like the entire goal after a while. Part of the challenge is reminding yourself where you want to get to, but not getting impatient with that. Patience is something I’ve had to put some deliberate focus on, because I can feel like I’m not moving fast enough, because it’s taken me a long time to get to where I am. I think back to how I started out programming for the first time, when I was about 12 years old. The process of doing this has felt a lot like that, and I’ve used that experience as a reminder of what I’m doing, as opposed to my expectations from the more experienced context I am trying to give up, from my many years in the “pop culture” of computing. When I was first learning programming, it took a few years before I started feeling confident in my skill, where I could solve problems more and more by myself, without a lot of hand-holding from someone more experienced.
I remind myself of a quote from “The Early History of Smalltalk”:
A twentieth century problem is that technology has become too “easy”. When it was hard to do anything whether good or bad, enough time was taken so that the result was usually good. Now we can make things almost trivially, especially in software, but most of the designs are trivial as well. This is inverse vandalism: the making of things because you can. Couple this to even less sophisticated buyers and you have generated an exploitation marketplace similar to that set up for teenagers. A counter to this is to generate enormous dissatisfaction with one’s designs using the entire history of human art as a standard and goal. Then the trick is to decouple the dissatisfaction from self worth–otherwise it is either too depressing or one stops too soon with trivial results.
What I see myself doing with this attempt is gaining practice in improving computing models. I’ve selected an existing model that is somewhat familiar to me as my platform to practice on, but it contains some elements that are utterly unfamiliar. It contains enough context that I can grasp basic building blocks quickly, making it less intimidating. (Part of this approach is I’m working with my own psychology; what will motivate me to keep at this.) This approach has also enabled me to push aside the paraphernalia of modern systems that I find distracting in this process, for the purpose of gaining practice. (I haven’t thrown away modern systems for most things. I’m writing this blog from one.)
You cannot plan discovery. So, I’m focused on exploring, improve my understanding, being receptive, and questioning assumptions. I’ve found that being open to certain influences, even ones that take me away from goals, in an unplanned fashion (just from events in life forcing me to reprioritize my activity), can be very helpful in thinking new thoughts. That’s the goal, improving the way I think, learn, and see.