Master the highest-leverage programming technique ever
Which of these qualities do you strive for when writing code?
Obviously, all of these are important, but they are often elusive. How can we make something complex more clear? How can we make long, intricate logic readable?
The answer is Domain Specific Languages. And Clojure excels at them. Lisps have traditionally been great at writing languages in. Why? Because it was originally defined in itself. That's right: Lisp was designed to be written in itself. That means from day one, you could use it, with no dependencies, to write languages in.
Languages have this ability to capture an idea in a really elegant way. Humans develop languages naturally. As you gain expertise in a field, you begin to pick up the jargon, the expressions, the turns of phrase. And as you learn to speak it, you learn to think it. The concepts become intuitive. Your language becomes concise, clear, efficient. So why shouldn't our languages let us do that?
Well, I think they should. And I think this feature of Lisp gives it its ultimate power. It's that Lisp knows it's not the best language possible for every use case. But it gives you the tools to build a language that does fit the use case.
How does it do that?
Well, that's what we'll explore in this course. You'll learn:
- The difference between an interpreter and a compiler
- How to write a a simple interpreter that uses Clojure's built-in data structures, like Hiccup
- How to turn any interpreter into a compiler
- How to leverage Clojure's syntax to shortcut your DSL development--and make writing an interpreter even easier
- That interpreters don't need to be complicated--in fact, they're nothing more than a conditional!
- How to write Turing-complete languages
- To write your own Lisp interpreter and compiler in Clojure
- How to add a new syntax to your language
- How to get started with Instaparse
- The basics of setting up a TDD workflow to really get into flow
Domain Specific Languages in Clojure
Want this course?
Love it or leave it guarantee
If you don't learn as much from this course as you thought, just ask for a refund within 30 days and I'll give you your money back.
1. Introduction to DSLs
We define the terms Interpreter and Compiler, and build a model of how they work.
4. TDD Setup Interlude
We take a break to set up a Test-Driven Development workflow that we can use from the command line instead of relying on a editor integration.
5. More tests for the evaluator
We continue to build out the test suite for our Hiccup evaluator.
6. From interpreter to compiler
We begin converting our Hiccup interpreter to a Hiccup compiler. That allows us to do compile-time optimizations.
7. Deeper in the hiccup compiler
We add more compile-time optimizations and use tests to drive them.
8. Our Lisp Interpreter Begins
Lisps are wonderfully simple languages. Writing one will teach you the basics of recursive evaluation and how to build a Turing complete language while leaning on the host language.
9. Function calls in Crisp interpreter
Central to Lisp is the ability to call functions. We are building a language hosted on Clojure, so we will want to call Clojure functions to be able to borrow what's already there. What's more, we will want to define our own functions in Crisp, so we develop a way to do that. All with TDD!
10. Crisp compiler
We take our trusty Crisp evaluator (interpreter) and step-by-step convert it into a compiler using a common and simple compiler technique.
11. Adding static scope information
We modify the compiler to add static scope information. Even though we don't know the value of variables until runtime, we do know the names of the variables at compile time. We can use that to make missing variables a compiler error.
13. Finishing the JS Grammar
We got started on a grammar in the last lesson. Now we'll finish it.