Been working on this recently: http://byte.school github
(the version on github is more up-to-date)
It's a beginner/intermediate course covering a bunch of stuff. The structure divides everything into "lessons" and "practicals". The lessons are a sequence of purely expository material; the practicals are a sequence of tutorials/demos/exercises. Each practical has a prereq of reading up to some point in the lessons. A really patient student could read through all the lessons before touching the practicals, but most students will probably prefer jumping back and forth.
At some point, I'll make video walkthroughs of the practical material, but for now I'm just sticking to text.
The core progression focuses around Go:
- bit representation of data
- hardware and OS basics
- assembly (for a fictional processor; kept very basic)
- my pedagogical intro high-level language, called Pigeon
- basic algorithms and data structures
- image formats and basic processing
- SQL (with sqlite and postgres)
So far, I have drafts of everything up through Go. Should take a few more weeks to draft the rest.
After the core progression, there will be more units which students can do in any order:
- x86 assembly
- x86 assembly and even MIPS assembly are weighed down by too many instructions and quirks.
- Java, even aside from its merits for day-to-day use, makes some very basic things unnecessarily difficult to learn (just consider the rules for how names get resolved).
- Python starts out well, but its more advanced features are weird and hard to explain; also, the transition from Python 2 to 3 presents many annoying distractions.
But why not start with C? Strictly speaking, C is a smaller language than Go: C doesn't have Go's slices, maps, methods, interfaces, packages, embedded structs, panics, nested functions, closures, goroutines, or channels. Aside from unions, pointer arithmetic, and the preprocessor, there's basically nothing in C that doesn't have a close analog in Go. However:
- Most of those things which Go has but C doesn't are very useful, especially to new programmers. Having slices (effectively dynamically-sized arrays) and maps out of the box gives Go a dynamic-language feel in a static language. C's lack of namespacing is archaic. Goroutines and channels, though perhaps not the end all solution to multi-threading, are the easiest introduction to multi-threading I know of.
- C's implicit casts potentially confuse learners about the whole concept of types and static typing. All Go casts are explicit (except when using implementing types of an interface), and Go doesn't have type aliases (aside from a few built-in ones, like 'rune' for 'int32').
- C naming conventions are ugly. The C standard libs (as well as Unix and Windows platform API's) use gross names.
- C's use of *  and () as type modifiers on declared names is the WORST SYNTAX CHOICE EVER. Go types are way easier to learn to read (even just putting names before types is a win).
- C's concept of declarations vs. definitions is confusing in itself, and the notation makes it 10x more confusing.
- The preprocessor is a language on top of a language, and the moment you start doing real C programming, real API's use macros in weird and scary ways.
- With Go, there's just one real choice of compiler, and its use on the command line is very straightforward. Compiling C very quickly drags in a bunch of distracting Windows and Unix shell syntax, build scripts, etc.
- Go has full-featured but mostly straightforward standard libs. Compared to other full-featured standard libs, the Go libs have a very flat organization. It's super easy to get lost browsing through the standard classes of Java, Python, Ruby, C#, and other OO languages.
- Go has fewer undefined behaviors and generally has better error messages. Runtime checks catch some very basic errors, like out-of-bounds indexing.
- Despite being garbage collected, Go at least has the programmer thinking in terms of bytes. Unlike with other GC languages, a user of Go knows exactly what every piece of data looks like in memory (bar padding and maps).
After learning Go, learning C (at least the language itself) is very straightforward. The student can focus on all those distracting ugly bits about C because they'll already understand most everything about the semantic core. The only major new semantic concepts coming from Go are manual memory management and pointer arithmetic.
There's often a concern that students need to start without garbage collection lest they never learn to cope without it. I don't really believe students get infected by learning one thing that makes them incapable of learning other things. What *can* happen, though, is that students get distracted and preoccupied to the point that they never learn things they should. Also, students can develop *confused* ideas that hinder learning new things: if, say, a student learns a GC language but develops an incorrect mental model of how it all works, that could hinder learning memory management. I think Go taught properly avoids that issue. Better that students learn the basics first, and then they can focus on memory management without distractions.
If there is one big down side to starting with Go, it's the terrible debugging story. JetBrains is putting out Gogland with a visual debugger, but it will soon be non-free, and I can't verify its quality. Alternatively, setting up a visual debugger requires editor plugins (and thus configuration headaches), and I've had mixed experiences.
Anyway, once these topics are all complete, I'd like to expand out to more topics, like say OpenGL, audio, data compression, encryption, GUI programming, FP, possibly some math topics, etc. Not all of this I'm qualified to cover myself, so hopefully I can rope in some other authors. For now, though, it's important to establish all this baseline material. If you see factual mistakes in the current version, let me know on github. At some point I'll take structural suggestions/patches, but for now I'd like to focus on filling in the missing pieces.
If someone wants to write the x86 material, that would be great. I also need help completing my fictional assembly: the idea is that our imaginary system has just one device, a terminal, but I can't decide what exactly the set of memory-mapped registers should be and how they should be handled. I want something as simple as possible but still representative of a real system (or at least a system that, in principle, *could* exist). I also haven't yet written any example code, and there's probably some essential instructions I've forgotten to include. Once that all gets sorted, it would be super cool to have a simulator for students to use, so that's another thing I'd love for someone to contribute.
Come to think of it, I also need to work a bit on the markdown conversion I'm using and fix the syntax highlighting (currently done in JS in the browser). Currently, neither of these things let me format things exactly how I want.
I don't know where any of this fits into HMD's plans, but at the very least it should be complementary!