I will preface this with, "I absolutely love SICP", but I think there are better places to go for everything it teaches. In terms of breadth and information per page ratio there's almost nothing better though.
- For how to actually program: How to Design Programs [1]
- For how to write programming languages: Essentials of Programming Languages [2]
- For how computers actually work: The Elements of Computing Systems (nand2tetris) [3]
I am a huge fan of How to Design Programs. It does a better, more thorough job of teaching the "core skill" of programming SICP or anything else I've come across. Its text is better paced (although very long), more approachable, and more focused on teaching how to program (as the name suggests). On top of that it has an absolutely massive number of exercises that do not require an interest in basic number theory to enjoy.
You will know how to actually solve new problems with programming when you finish HtDP (or are even part way through).
The really interesting parts of SICP are the 4th and 5th chapters any way right? Well, Ch. 4 (about "metalinguistic abstraction" or how to modify your interpreter to give your language different properties) is incredibly densely packed with information. It covers an interpreter for all of this in about 100 pages:
- lazy evaluation by default
- nondeterminism
- aka search as a language primitive
- the basics of continuations (but not accessible to the user)
- propositional logic
- aka declarative (what is) vs imperative (how to) programming
It's a great appetite whetter, but it'll leave you wanting more.
If this stuff interests you Essentials of Programming Languages is a much better resource. It follows a similar style to the approach in SICP. You are walked through building interpreters for increasingly more complicated languages. Here are some brief descriptions of some of features you'll learn to add to the LET language (a very simple purely functional language that serves as the core, you'll also write this too):
- Mutually recursive procedures
- Static code transformation to use lexical addressing
- Mutation (and how to perform call-by-value, call-by-ref, and call-by-need languages)
- Continuation-passing vs Environment-passing interpreters
- Advanced control structures like exceptions and threads
- Static types and type inference
- Typed modules (I've not seen any other introductory text do this)
- Object-orientation
It contains a plethora of great exercises that ask you to extend the languages in interesting ways or to reflect more realistic demands of a language.
For chapter 5, Register Machines, I think we all know that the first half of Elements of Computing Systems (colloquially nand2tetris) is a great resource for that. The second half is excellent as well, but it's outside of the SICP scope.
Summary:
;; Page Count 1374 = (+ 792 410 (/ 344 2))
In total I just recommended 1374 pages to replace 657 pages in SICP, so it's double the effort. And to be honest, if you're motivated enough to read all that then, you'll probably still read SICP if for nothing else than historical interest. If you start with SICP though, you'll still get a lot out of all the recommendations though!
[1] HtDP: http://htdp.org/2021-11-15/Book/index.html
[2] EoPL: https://eopl3.com/
[3] nand2tetris: https://www.nand2tetris.org/