Decided to buy it anyway, primarily because the two implementations of the toy language piqued my interest (most other books only go through one implementation).
Boy was I glad I did.
Writing is superb, end-of-chapter challenges are instructive, code samples are meticulously and beautifully done. Here and there I also encountered references to other languages (e.g. had a brief detour to Lua internals [1], thanks to a side note), which I have enjoyed too.
Went through the first half twice: first using Java as it is written and then secondly implemented the interpreter in Rust (my initial fear turned out to be unfounded). Currently going through the second half.
So if you're reading this, thank you, Bob :).
Two books I enjoyed despite, rather than because of the language:
"Program Generators with XML and Java" (aka "program generators for fun and profit") https://www.amazon.com/Program-Generators-Java-Craig-Cleavel...
And:
"Designing Active Server Pages" https://www.amazon.com/Designing-Active-Server-Pages-Mitchel...
The latter mostly for techniques to keep programs structured even when writing mostly in template soup (like php, asp, coldfusion etc).
(I don't know how to answer your question accurately)
https://www.youtube.com/playlist?list=PLRAdsfhKI4OWNOSfS7EUu...
haven't tackled the second part, I'm currently down the rabbit hole with Samuel Mimram's Program = Proof, which I'm just loving and the first book I've seen that focuses on the Curry Howard Isomorphism, covering essential logic, lambda calculus, type theory, and Agda... so good.
It gets a bit trickier when you are nesting environments for closures and the like, but certainly nothing insurmountable without a few things from the standard library.
This was my goal anyway: implement Lox differently. Off the top of my head:
* I used ADTs to encapsulate error types and states as much as possible, this lead to heavy use of enums and pattern matching. * I implemented the `Environment` as an immutable, explicitly passed value. This was a little challenging at first, but overall quite satisfying to do.
I think it is refreshing he uses a "boring" language.
Just that I am more familiar with other languages and I worried I'd get lost trying to understand Java and not digest the material properly. I tend to fall down rabbit holes sometimes ...
If anything, this has been another way for me to be more acquainted with Java, which I am happy for :).
A boring language would be a language where you aren't triggered/bothered by its basic ergonomics and hygiene. In Java, you must translate discrete variants into dogmatic OO paradigm instead of using language primitives.
Thanks to Java's poor design, it's fundamentally a hazard and a land mine thanks to null not being first class in type system. The use of null as a value inhabiting any type should result in a slap in the face upon opening a pull request (until Java adopts type-safe optionals like Kotlin)
P.S. Switch expressions and sealed interfaces aren't there yet, Kotlin and Scala are still the only options if you are imprisoned to JVM.
* new books should use more modern popular languages to age well and to attract younger audience
* on the JVM there are languages better suited for the topic (e.g. Scala by a large margin)
* There's already the "Language Implementation Patterns" book (https://www.amazon.com/Language-Implementation-Patterns-Doma...) which not only is great for teaching because of its pattern-based approach but also gives you real life skills (e.g. ANTLR is used in most big data query engines)
That doesn't make sense. Java is one of the most popular programming language, more popular than a lot of other "modern popular languages". The only languages more popular are either as old as Java (JavaScript, Python) or older (C, C++). C# might be one option, but it's close to Java, and I don't know if the support for Linux/MacOS was here at the time this book was started. Same thing for JavaScript and Python's popularity, which are relatively recent trends. On the other hand, Java has stayed and endured. It's still here and will still probably be here in 20 years.
> on the JVM there are languages better suited for the topic (e.g. Scala by a large margin)
Scala is less popular than Java, and harder to master. Once you consider Scala, you get into endless arguments about whether to use Scala, SML, OCaml, Haskell, etc.
> There's already the "Language Implementation Patterns" book
There is also "Writing an interpreter in Go" and "Writing a compiler in Go" that seem to cover the same ground. But I'm certain than more people know about interpreters and compilers thanks to these three books than thanks to any one of them.
> but also gives you real life skills (e.g. ANTLR is used in most big data query engines)
The majority of programming languages don't use a generated parser but a handmade recursive descent parser, because it's the best at error reporting. There's a good post about different parsing approaches by Laurence Tratt: https://tratt.net/laurie/essays/entries/which_parsing_approa.... Generated parsers are great for DSLs but that won't teach you how the programming languages that you use work.
A lot of young people know Java because it's what they learned in school and because a lot of employers use it so it's the first thing they learn on the job.
In my opinion, the language is designed in a way that makes it easy to pick up for beginners. The lack of obfuscated operators and foot guns is a plus, and the language forces you to avoid doing 'clever' code golf things, too.
And frankly, Scala’s build tool is way too slow and would be a slog to use for this book.
> Language Implementation Patterns
I hear the code examples are in Java. I think that's unfortunate. (And a bit ironic about your post).To me, it was a missing piece of the big puzzle of "how do computers work". I read many a book to answer this question, and came away with three books:
- CODE by Charles Petzold explains the CPU
- Operating Systems: Three Easy Pieces by Arpaci-Dusseau explain OSes
- Crafting Interpreters by Robert Nystrom explains programming languages
Masterfully done.
From my own experience, some examples might be:
- SICP, by Abelson and Sussman
- Paradigms of Artificial Intelligence Programming, by Norvig
By general acclaim, some examples might be:
- How to Design Programs, by Felleisen et al
- Beautiful Racket, by Butterick
- LISP In Small Pieces, by Queinnec
- The Art of Computer Programming, by Knuth
- The Elements of Computing Systems, by Nisan and Schocken
What else comes to mind? (My list is very LISPy, but that's just bc I have high awareness there.)
I will check out Three Easy Pieces! Thanks for the rec.
Compiler Construction Using Java, JavaCC, and Yacc[0]
In my opinion, this has been underrated book. I learned a lot from this book. I enjoyed it and plan to read it again.
If you could check it out. It contains solid explanation from theory to implementation. I'm not affiliated with the author, I just wanted to show I'm grateful for his work.
[0] - https://onlinelibrary.wiley.com/doi/book/10.1002/97811181127...
PS: you might want to reformat your list.
If you are into videos: https://www.youtube.com/playlist?list=PLSE8ODhjZXjbohkNBWQs_...
Doing the tree-walk interpreter (the first “half” of the book) took two weekends, working much of the day on Saturdays and Sundays.
When I went through it, I built my interpreter in another language, and added other features as I went along. Both helped solidify the concepts. I often had to pull out my additional features as I learned better in subsequent chapters; getting to see the difference between my amateur approach versus the author’s experienced methods was quite helpful!
The author has also kindly made a web version of his book freely available:
On a minor note, whenever I read a technical book I always come across some mistake in the copy-editing, a missed article, a misplaced article, a typo. I'm 70% of the way in and haven't come across any! Which again speaks to the well-craftedness and hand-craftedness of the book!
Though I do feel the author ran out of steam with respect to the lovely, quirky artwork in the book. I'm disappointed Bob, I want my money back.
Definitely true. I was hoping to write the book in two years and it took me six. By the end (which was also during the pandemic) I was pretty fried and it was hard to summon much energy for whismy. I tried to pack as many illustrations in as I could, but by later chapters I was really just trying to get it done.
Also, pragmatically, I found the chapters on the bytecode VM really needed a lot more useful illustrations for things the execution flow so that left less space for the fun ones.
> I'm disappointed Bob, I want my money back.
Sorry, all sales are final. I've already spent the money on whiskey and synthesizers. That's what you get for not scrutinizing the later chapters on the free web version first to ensure that they were up to your standards.
when i was given a compilers class to teach i wanted to teach hand-written recursive descent because it is simple, elegant and makes the recursive nature of grammars so readily apparent without hiding concepts behind obscure tools
i was despairing that there were no good books around on recursive descent, but stumbled on crafting interpreters before it was even released as a book and immediately structured my class around it
absolute life saver and my students all love it
cannot say enough good things about it!
Also, I'll second the comments here: The design of your blog is beautiful.
And if anyone was curious like me to see the book's website, here is a archive link: https://web.archive.org/web/20220616005500/http://www.crafti...
This is really useful in setting expectations.
I'm following teachyourselfcs.com[2], so I'm hoping to solidify the concepts the book intro'd by reimplementing the bytecode interpeter in Rust (rlox) and following Alex Aiken's lectures online. OP appears to have reimplemented Lox in Golang (glox) for reinforcement.
So far the time has been well spent. Compilers are now much more 'just another program', and I have new conceptual tools for tackling software engineering problems.
1. https://github.com/thundergolfer/uni/tree/main/books/craftin...
There exists various flavors of the book (in ML, Java, and C).
1. Peter John Brown. 1979. Writing Interactive Compilers and Interpreters.
2. Timothy Budd. 1987. A Little Smalltalk.
3. Ralph E. Griswold. 2000. The Icon Programming Language, 3e.
4. Pat Shaughnessy. 2013. Ruby Under a Microscope: An Illustrated Guide to Ruby Internals.
This is a new one I haven't read yet, but it looks very practical:
5. Clinton L. Jeffery. 2021. Build Your Own Programming Language: A Programmer's Guide to Designing Compilers, Interpreters, and DSLs for Solving Modern Computing Problems.
I don't know anything about this one, but people have told me they prefer the first edition:
6. Ronald Mak. 1991. Writing Compilers and Interpreters: An Applied Approach.
The other big realisation is just how freaking enormous that class of problems is.
This adds another layer of difficulty, depending on how comfortable you are with Java and the language of your choice, but it was the way that worked better for me. I truly felt that I created a language, not just copied some code.
In my case, I chose to use C++ (more specifically, C++20 under GCC 11) with minimal external dependencies (currently just Boost for memory mapped IO and fmt). I'm working on the second part of the book now (only just getting started, really). My tree-walking interpreter works, but does have some limitations (I currently leak memory in a few locations, such as closures, due to reference-counting cycles).
I've had a few gotchas following along this way, but it's been entirely due to my choices.
One thing I'd highly recommend doing: setup tests to validate your code immediately! I don't have anything fancy setup, but basically what I have is mechanisms to hand a script to my interpreter and capture stdout/stderr. It's not the best level of testing, for sure, but it does allow me to basically place the lox snippets from the book into a reproducible framework to make sure my interpreter is doing what's expected. I also added tracing/memory dump facilities to my tree-walker to facilitate debugging as well (the byte-code interpreter builds this in from the start, so I've not deviated in that aspect, yet).
I'm really enjoying the book. It's definitely created a spark in me I've not felt in quite a while.
It also helps to have a practical application that you're working towards - I was in the process of building a game server in Node and wanted a way to modify the state at runtime, so I used this book to build an embedded language for debug builds.
I used the book by translating the code into my favorite programming languages and heavily played around with different approaches, which is way more time consuming but I don't think it is necessary at all.
I think if you are already a seasoned programmer you might find it even useful to just read the book and the code without doing much of coding at all. Given that you are good at understanding code without executing it.
Then I did it again with a different destination language design, and had to reference the book less.
Whenever I end up writing an interpreter for a third time, I'll probably be able to do it without referencing Crafting Interpreters at all, if I had to. But I expect I'll still check the book before doing certain fiddly parts of the parser.
Then I would try to implement it on my own without reading, having Chapter 3, the language specs, open in a different tab for reference. I chose Java because (1) I wasn't very familiar with it, and (2) I wanted the option to use their code snippets.
My goal is to pass all of the edge test cases the book mentions. So finally I would read the chapter and take some notes on the different design choices and test cases I missed. The reading is quite fast once you've already implemented because you know what to look for.
it worked well for me.
i think this book along w/ spivak's calculus is probably tied for position #1 in technical book that i found most enjoyable and had the biggest influence on me. cannot recommend it enough.
edit: i should mention if i got stuck i went back to reading the book! the point was, "read to understand, try to implement, if understanding not there, repeat". rather than just typing directly out.
It's definitely well-written and you can feel the love and care that went into producing it. But I think it would have been stronger had Nystrom skipped the Java version and spent those pages on theory instead before jumping into the C implementation. While going through the Java stuff (implementing in C# instead because I have an emetic reaction to Java) I found myself just wanting to hurry up and get to the good stuff in C. And I found the visitor pattern and code generation stuff to be a distraction.
The code can also be a bit hard to follow at times because he jumps around from file to file and function to function. He'll present a function that calls a function that doesn't exist yet, and then define the new function, etc. He does it that way because he wanted to ensure that every single line of code was present in the book (he wrote a tool to ensure that was the case), and it certainly helps if you're trying to copy his code verbatim, but not so much for high-level understanding. Maybe that's a failing on my part.
Finally I wish he had gone into more detail on how one might implement a register-based VM because they are faster and more interesting (to me) than a stack-based one.
With Truffle you start with a Java based tree walking interpreter (could use Kotlin too for less boilerplate), so the very easiest stuff. Then you annotate it in some places, add a dependency on Truffle and ... that's it. Now you have a JIT compiler for your interpreted language. The next step is to start refining the use of the annotations and to write specializations in order to accelerate your JIT by incorporating standard techniques like polymorphic inline caches.
Finally you can compile your new interpreter+JIT engine down to a Graal native image (single standalone native binary), thus ensuring it can start as fast as an interpreter written in C and can warm up as fast as V8. The binary can also be used as a shared library.
Given that this tech exists now, people who choose to write their interpreter in Java will have a massive edge in performance over people walking the traditional path. It's still relatively new technology but it's hard to see how it doesn't change interpreter design forever.
- Manual Memory Management Is Hard. Interpreters are complex pieces of software, you don't need another rabbit hole to dive into while you're learning your first parser. You don't need to agonize over where to put the contents of the file buffer you're parsing before writing your first lexing switch. People spend years with C and C++ and still get MMM wrong. The book is supposed to be fun.
- Data Structures Are Hard. This doesn't apply to C++ or really any modern language, but since you wanted it done in C the first time, that would entail the obligatory "Implement your own universe from scratch" exercise C is infamous for. I don't mind, I always like implementing my own universes (although I despise C even more than Java, it can't be over-emphasized how badly engineered that language is). But again, Pedagogy says that you should introduce the minimum possible surface area while approaching a new topic, ideally a single topic at a time.
- Interpreters Should Be Fast, so overly dynamic languages like python and javascript are out.
- A teaching language should be popular and familiar. The obvious benefit is accessibility to as many learners as possible, but a less obvious one is the availability of tools and cross platform support.
Out of the vast array of available programming languages and their toolchains, the combination of GC, powerful standard library and reasonable performance excludes a whole lot. The popularity requirement basically only leaves Java, C# and Golang standing.
If you would have prefered I pick Go, you'll definitely like Thorsten Ball's two books.
Choosing a language for books is really hard these days. There are so many choose from and most are quite large and complex, so it's hard to find a single language that is familiar to a large enough segment of the audience.
https://www.wired.com/2013/01/comedy-in-the-hacker-world/amp
I was lucky enough to be working on the Dart team in Seattle at the time with Bob, who is one of the smartest, funniest and nicest people I have ever met (although the whole team was an exceptional bunch).
But I think it could be more straight forward if the first interpreter didn't use Java, but a modern main stream language that can avoid code generation and Visitor Pattern. Those parts feels like unnecessary detour.
Yeah, a language with nice algebraic datatypes and pattern matching does eliminate the need for Visitor patterns.
But the reality is that many programmers are working in object-oriented languages where the Visitor pattern is useful, so I deliberately picked Java in order to teach that approach. The book tries to meet you where you are and not where you might wish you were.
- The C Programming Language (original C book)
- The Art of Computer Programming (TAOCP)
- Structure and Interpretation of Computer Programs (SICP)
- Compilers: Principles, Techniques, and Tools (Dragon book)
- Don't Make Me Think (design)
- Zen and the Art of Motorcycle Maintenance
Grune’s Parsing Techniques and Morgan’s Building an Optimizing Compiler are okay.
You might also like Charles Petzold's book Code.
The Elements of Computing Systems - Noam Nisan, Shimon Schocken
Operating Systems: Three Easy Pieces - Remzi H. Arpaci-Dusseau, Andrea C. Arpaci-Dusseau
It's one of my all-time favorite books about programming, I keep coming back and re-reading parts of it.
The author does excellent programming education through fun videos.