Learn Lisp
ZParen has a course in Common Lisp. It follows the order of Paul Graham's ANSI Common Lisp and On Lisp, from your first form to a query compiler. The text and exercises are ZParen's own, and each lesson names the chapters to read. It has four levels:
| Level | Lessons | What you write |
|---|---|---|
| Beginner | 1 to 8 | A function per exercise: forms, recursion, lists, data |
| Intermediate | 9 to 15 | Closures, I/O, packages, numbers, macros, conditions, CLOS |
| Advanced | 16 to 21 | Two projects: a spreadsheet, and a shop's inventory |
| Master | 22 to 28 | Two projects: a test framework, and a query engine with its compiler |
You learn in a real project. Each exercise is a function you write in a file, and FiveAM tests check it. Git records your progress.
Start the course #
On the Welcome screen, click Learn Lisp…. In an open project, choose New Lisp Course Project… from the command palette, or choose the Lisp course template in the New Project dialog.
After you start a course, the Welcome screen shows Continue Learning Lisp instead of Learn Lisp…. It opens your most recent course project where you stopped. The Learn panel in another project and the palette's Continue Lisp Course do the same. To start a second course, use the New Project dialog.
When you finish every lesson, the Welcome screen stops offering the course. If a ZParen update adds lessons, it offers the course again. To hide it before then, turn off Settings ▸ General ▸ Learn Lisp. The course stays in Help ▸ Learn Lisp and the command palette.
The course uses ocicl to pin FiveAM, the test framework. If ocicl is not set
up, the dialog says how: run brew install ocicl, then ocicl setup. The
first time, ocicl downloads FiveAM, so you need the network once.
The new project holds lesson 1 only:
| File | What it is |
|---|---|
src/01-forms.lisp |
The lesson's exercises, each a commented task above a stub defun |
tests/01-forms.lisp |
A FiveAM test per exercise. Read it: it says exactly what each function must do |
learn-lisp.asd |
The systems learn-lisp and learn-lisp/tests |
.zparen-course |
Marks the folder as a course project |
The Learn panel #
Help ▸ Learn Lisp, or Toggle Learn Lisp in the palette, opens the panel on the right. It shows the levels and lessons, with what you have finished. Click a lesson to read it. Reading a lesson never changes your files.
The right panel also holds the assistant, Documentation and the Inspector. When one of them replaces Learn, a tab row shows at the top of the panel. Click Learn, or press ⌃Tab, to go back to the lesson where you left it. ⌃⇧Tab goes the other way.
A lesson has prose, examples and exercises. Hover over an example and click Run, or focus it and press ⌘↩, to send it to the REPL. ⌘[ and ⌘] move to the previous and next lesson while the panel has focus.
To make the panel's text larger or smaller, use Settings ▸ General ▸ Panel font size. The Assistant uses the same size.
To work on a lesson:
- Click Start Lesson. ZParen saves your files, makes the branch
lesson/NN-namefrommain, and opens the lesson's file. - Replace a stub with your answer. Evaluate it with ⌘↩ as you go.
- Click Check on the exercise, or press ⌘↩ in its card. ZParen saves the file, compiles and loads it, and runs the exercise's test. A stub shows as Not written. A failing check shows FiveAM's reason. Compiler notes go to Problems.
- When the test passes, ZParen runs it again in a new SBCL that has only your files, then commits your file as "Pass 1.1: Twice". Your running Lisp can hold definitions the files do not, such as one typed in the REPL or deleted from the file since. When the test passes only because of one of those, the card says so and nothing is recorded. The new SBCL uses the files your Lisp already compiled, so this takes about a quarter of a second, and it leaves your running Lisp as it was.
- When every exercise passes, click Finish Lesson. ZParen merges the
branch into
mainand adds the next lesson's files there.
Each card also has Go to Code, Hint, Show Solution and Ask Assistant…. The solution shows in the panel only, never in your file. Ask Assistant sends the exercise and the last check's result to the assistant, and asks for a hint rather than the answer. ‹ Back to Lesson in the assistant's header returns to the lesson.
⌘U runs every lesson's tests. Exercises you have not written yet are skipped, not failed.
Projects #
From lesson 16, each level builds programs. A project runs over a few lessons, and each exercise adds a working part to it. Later lessons call the functions you wrote in earlier ones, so your own code is what the project runs on.
| Project | Lessons | What it teaches |
|---|---|---|
| Spreadsheet | 16 to 19 | Formulas as trees, a queue and recalculation order, formulas compiled to closures, memoizing, and speed with declarations |
| Inventory | 20 and 21 | Finding bugs with the debugger, trace and the inspector, then subclasses, print-object and :after methods |
| Test framework | 22 to 24 | Macros that see code, gensyms and capture, a test runner that uses restarts, and macros that write macros |
| Query engine | 25 to 28 | Pattern matching, a query interpreter, a compiler that turns queries into Lisp, anaphora, places and a read macro |
You can start any project on its own, without the lessons before it. In
the course outline, click Start Project beside the project's name, or
open its first lesson and click Start Project there. ZParen adds that
lesson's files on main, and makes its branch. The lessons you skipped stay
where they were, and Continue goes on with the lesson you started last.
Each project uses only its own code and Common Lisp, so you need nothing from
the lessons before it. Lessons inside a project build on each other, so a
project starts at its first lesson.
Each project has a package of its own, named after the course and the
project, such as learn-lisp.query-engine. Its first lesson's file starts
with the defpackage. When you start or continue a lesson, the REPL and the
package box in the title bar switch to its package. After a restart, ZParen
loads the course first, so the package exists. A name in one project
never clashes with a name in another, or in lessons 1 to 15.
A project lesson can give you code. It is at the top of the lesson's file, and Provided code in the panel shows it. The lesson header says which part of its project the lesson is, such as "Project: Spreadsheet, part 2 of 4", and the course outline groups the lessons under their projects.
Some exercises start as code with a bug, not as a stub. Their card says To fix, and Check fails until the code is right. Lesson 20 is all such exercises: call the code in the REPL, read the debugger, trace the functions, inspect the objects, fix the code in the file, and use Restart frame.
Progress is git #
| Event | What git records |
|---|---|
| The project is made | "Start the Lisp course, with lesson 1" on main |
| Start Lesson | A branch, lesson/NN-name, from main |
| An exercise passes | A commit "Pass N.K: Title", with the trailer Exercise: NN/K |
| Finish Lesson | A merge into main with the trailer Lesson: NN, then "Add lesson N+1" |
The panel reads progress from these trailers, so it stays right after a clone, on another Mac, or after you commit by hand. Show Course History in the panel's ⋯ menu shows the graph in the Output panel.
Before it switches branches, the panel saves your files and commits any
unfinished work as "Work in progress on lesson N", so switching never loses
typing. It switches only between main and lesson/* branches, and asks
before it leaves a branch of your own. It never resets, deletes or pushes
anything. If a merge stops on a conflict, the panel says which files, and
offers Abort Merge.
When a ZParen update brings more lessons and you finished every lesson in your
project, the course view offers Add Lesson N. It writes the new lesson's
files on main in one commit.
Lessons 1 to 15 run in the learn-lisp package, and each project in its own
package. They use only Common Lisp. Your other projects are never touched.