As I was growing up in England in the 80s, there was a boom in home microcomputers, with the Commodore 64, the ZX Spectrum, and the BBC Micro being three popular choices. These provided an excellent and approachable introduction to programming, with many of my friends learning programming in BASIC and assembler. We taught ourselves the fundamentals of computing while we were playing, and at a relatively early age.
These days the computing environment is complex, and it's much harder for a beginner to get started, or even know how to get started. Mostly programming is learnt at university or in other formal education. While there is definitely more to learn now than before, it seems like the fundamentals of coding should still be easier to pick up than it currently is.
This post takes a look at what made home micros effective learning environments, and considers what a modern equivalent might look like.
Properties of 80s home computers
When a home micro powered on, typically you were dropped into a BASIC interactive session. This is significant: programming is how you interact with the machine, and there's no obvious choice about which language or environment you use to program in. Even if you wish to play a game (stored in those days mostly on tape), you've got to type some commands in to do so.
BASIC has a bad reputation and was probably never the best choice, but the versions of BASIC provided had some good properties for learning that modern programming environments lack. First, they weren't designed for large-scale software engineering: they had no complex datastructures, no modules or packages, no object-orientation, no testing facilities, no static typing. These omissions are perfect for an introductory environment, where the fundamental skill being learnt is writing code that does something rather than writing it to be clean and maintainable.
The machine model was simple and concrete. Display memory was available at a fixed location, and could be modified directly. There was no memory protection. This simple model would be considered anethema today: it has security and safety problems, is inflexible, and uses magic numbers (pointers to hard-coded addresses) everywhere. Also, modern graphics hardware is much faster but more complex, making memory-mapped display memory an inefficient choice. But for learning it's perfect for understanding memory, pointers, indirection, and data layout. The Commodore Amiga (launched later in the 80s) provided more complex and advanced choices, but still many display and sound features were programmable by setting hardware registers, which were mapped at fixed memory locations.
Apart from direct memory access, BASIC provided simple-to-use functions that could draw coloured points, lines, circles, and make simple sounds.
Today's computers typically separate consoles from graphics windows, but the micros had a single screen on which both appeared. I think this is also a good feature for learning: one can either output text or draw graphics without having to do any bureaucratic setup. An advantage of separating text console from graphics is that it makes it possible to pipe the output of one program into the input of another (for example, using a unix pipe), but this isn't important for a beginner's programming environment.
Assembler, or machine code, was readily available. Some, like BBC BASIC,
provided a way to interleave assembler instructions in your BASIC code,
which would assemble into memory (at
P%, an integer BASIC variable)
when run, and could be executed later. Others required the use of a
separate assembler to generate the machine code (although assembling the
code by hand was also popular), and simply provided a way to call
the code (typically returning the value of one of the registers back
to the calling code). Today, I don't think this is so valuable for an
introductory environment: computers are much, much faster, obviating the
need to use assembler at all, and today's processors are much, much more
The modern introductory programming machine
Whether this is an actual machine, or running on a regular PC, this is a sketch of what I think the experience should be:
You turn the machine on, and you get a blank screen, with a short introductory message and a prompt. You can type commands, or enter a built-in editor. It can't run anything else.
A program is a single file, which you can load, edit (using the built-in editor), save, and run. There's a separate area for loading pre-existing code that you can copy from, to paste into your own code.
There are hardware registers, a memory-mapped screen, and a programmable sound device. There's a mouse pointer, whose state can be read from hardware registers, and the pointer image is a memory-mapped sprite. There's a default font that provides coverage of most international unicode characters. There's a variety of different configurations, providing different screen resolutions, sound devices, and other options. Options might include sprites, or horizontal/vertical pixel scrolling, the Commodore 64 SID chip, or a programmable 16-channel synthesizer. Or the addition of games controllers or a touch sensor. Each configuration comes with a description of where in memory each device is mapped to, and these addresses and values are also made available as named constants to the programming language.
The programming language has types, pointers (including hard-coded values to the hardware memory-mapped area), and built-in functions for simple interaction with the screen and sound, including graphics and text. It has functions, floating-point, sized unsigned and signed integers, a builtin bigint, and the standard maths functions. Strings are builtin and are unicode by default. Arrays are bounds checked, growable and shrinkable, and passed by reference. There's a record type (struct). There's no concurrency. This doesn't specify the language entirely, but the philosophy here is to make the language as safe and convenient as possible, but erring on the side of making it easy to hack around with, even if that sacrifices safety.
Storage appears simple: the machine has its own file system (stored on a memory card), with directories and files. Interaction with it is primitive: save this block of memory as a file, load this file at this memory location, load or save a program, list directory contents.
This machine is great for creating interactive playthings: 2D games, digital art, or animation. By providing a simple direct interface to the display and sound in the programming language, it gives a gentle start, but pushes the programmer to learn and use the memory-mapped hardware registers to get more advanced features, thus forcing them to learn low-level programming.
It teaches programming, but deliberately avoids teaching software engineering, in order to encourage playful experimentation. One concern is that by learning bad habits early, one will struggle later to develop good ones. I don't think that's a real concern; the beginner is not ready for the rigors of good practice, correct abstraction, separation of concerns, unit testing, and so on. Perhaps after learning to code on this machine, one is in a better position to learn software engineering principles having seen the messes they've got themselves into.
The choices of configuration provides flexibility at the cost of increased complexity. I think it's a good trade-off: in a teaching environment, a fixed simple configuration can be used, and more complex configurations can be introduced later. Competitions could be run to create the best experience in very limited configurations. More interesting or impressive experiences can be built in advanced configurations.
This machine doesn't interact with most of the modern programming world: it's not designed for mobile or web (although perhaps programs that you write can be run on either, given suitable configurations) and there's no network available. I think that's a necessary concession to keep things minimal, and it's possible to build interesting things without them.
Comparison with current programming environments
Many attempts at teaching programming using a graphical language have been tried, MIT's Scratch being the best example. The idea of graphical languages is to minimize the initial barrier to learning computational thinking, and to enable learners to produce something interesting almost straight away. I think Scratch achieves this ambition, and perhaps can serve as a stepping stone to fuller text-based programming.
Processing (and earlier Logo) aim for some of the same things as my proposal. Processing makes generating graphics and interactive experience easy, and does it well. The design prioritizes generating interesting art over learning programming fundamentals, and doesn't so easily get the learner over the hump of pointers, memory, and other lower-level details. I think one design choice that Processing makes is particularly good for beginners: to base the language on a pre-existing language (Java), but both cutting features from it, and extending it with more builtins. This makes it a very nice stepping stone towards learning full-blown Java.
Python is a wonderful language that spans from learning to professional use. The Python ecosystem is large, and so makes it harder for a beginner to approach. And because it is a very high-level languge, it also has the same problems as Processing of teaching low-level fundamentals.
Modern "industrial" languages: Go, Java, C++ expose more low-level details, but are hard to just hack and have fun in because each enforces software-engineering good practice, like static typing, object orientation, packages, source file layout, and so on. And while it's possible in each of these languages, setting up an environment where you can draw graphics and make sounds is some work, and there's no clear place for a beginner to start.
PICO-8 is the closest modern equivalent to an 80s computer. It provides a tiny 128x128 16-colour display, has chip sounds, and "hardware" sprites. One can write programs for it directly in Lua.
Other choices are functional languages (for example, Haskell, Scala, or Ocaml), smalltalk variants (for example, Squeak), or logic languages (for example, Prolog). I think these are interesting languages, and well-suited for certain domains, but I don't think they're ideal to start with, and I don't see a natural path for learners from them to mainstream programming. I think it's much better to learn one or two of these seriously later.
A final note
I've been thinking about this for some time, and while I don't think my ideas are fully baked, I thought it was time to put them in writing. Let me know what you think in the comments section below!