Iām going to be participating in the Recurse Center one week mini-batch in January. Recurse Center describes itself as āa self-directed, community-driven educational retreat for programmers in New York City.ā For the mini-batch, my proposed project was to 1. learn some lisp and 2. generate SVGs of jigsaw puzzles, for laser cutting.
Why these projects?
Iāve been wanting to learn Lisp for a while, because I thought that it would be a different kind of problem-solving experience - I like making my brain bend and the Lispers I know have different problem-solving instincts than me. I was also curious about it from a historical POV. Apparently itās been around since the 1950s? Though I guess that depends on which dialect weāre talking about; Common Lisp, which is what Iām learning, dates from the 1980s.
Why generate SVGs? Because itās relatively straightforward (Iāve done SVG generation before) and learning how to print things is a standard newbie-programmer task. Also, I adore generative art, and I love puzzles. I was heavily inspired by the work of inconvergent and nervous systems.
This post isnāt about jigsaw puzzles, though, itās about my first experiences learning Lisp. Here are some jumbled thoughts!
Iām following The Land of Lisp. The book isā¦hrm. Interesting. I grumbled about it on Twitter. First of all, the illustrations are adorable.
The pedagogical approach of the book also works really well for me - I enjoyed following along with the beginning exercises. Ideologically, though, Iām a bit annoyed. The book spends a lot of time assuring you that Lisp is ābeautifulā and āpureā and āelegantā, and also warning you that Lispers can be judgmental assholes, though it doesnāt use those words. Arrrgh.
The clisp REPL also includes this somewhat baffling ASCII art, which I kind of love:
Anyway, here are some unordered musings:
-
In Lisp, you can get the head of a list with a function named CAR (Contents of the Address Register) and the remainder with CDR (Contents of the Decrement Register). Look, they even have a Wiki page. Iām given to understand that many modern Lisps just call these FIRST and REST or some such English-language thing. I think itās pretty interesting that 1. they compose well and 2. you canāt just get the nth element of a list.
-
Relatedly, implementing my own
randselect
function looked like this:
(defun randselect (somelist)
(let
((n (length somelist)))
(car (nthcdr (random n) somelist))
)
)
-
How on earth does performance work in this language? Iām super curious.
-
I want to make tongue twisters out of cadddraddadar. No, seriously, itās adorable that you would name a function after the actual registers involved but I think this is one of those, uh, historical quirks that shows Lispās age. Someone sent me a link to this HackerNews discussion which actually seemed quite reasonable all-around.
-
Iām dying to know how you write maintainable, shareable code in Lisp that lots of people can work on who arenāt all inside each otherās heads. The way that Land of Lisp is encouraging me to write code - where you basically have lots and lots of unnamed heterogeneous lists where each element does Something Special and functions expect things in some weird undocumented order - is EXACTLY how I like to write python scripts for my side projects. As came up in my RC pairing interview, however, that approach is extraordinarily bug prone. It also takes forever for someone new to learn it. So ⦠Iām curious. But so far at least it matches how I think :) (Fast to write, infuriating to debugā¦)
-
These function names are a little weird. MAPCAR, really ??? I get it but also Iām coming in with pleeeenty of my own biases and this feels weird.
-
Evidently using APPLY is dangerous ?? But itās so useful! Gah! I canāt wait til we get to tail recursion.
-
Someone suggested I switch to using sbcl for my REPL; apparently it is More Efficient. However it lacks clispās awesome autocomplete in the REPL, at least on Mac.
-
The usage of association lists (alists?) confuses me. Is there anything that distinguishes an alist? How do you tell if something can be used as an alist?
-
Figuring out how to print stuff is weird. String interpolation is confusing. I thought that FORMAT was the answer but then it always prints to screen instead of returning a value? Unless you pass ānilā as the first argument, apparently. Should I be using CONCATENATE? I ended up finding a useful resource here.
" If the string will be constructed out of (the printed representations of) arbitrary objects, (symbols, numbers, characters, strings, ...), you can use FORMAT with an output stream argument of NIL. This directs FORMAT to return the indicated output as a string. "
- Random apparently doesnāt have a time-based seed ?? At least not by default. You have to seed it yourself, laÅ StackOverflow:
(setf *random-state* (make-random-state t))
- The syntax for LET is weird. I was confused about how to set up a variable that depends on another variable, e.g.
a = 5, b = a + 1
. It turns out that you need to use LET* for this? I got this from Practical Common Lisp.
(let* ((x 10)
(y (+ x 10)))
(list x y))
At the end of my very early flailings about, Iāve made it through six out of twenty chapters of Land of Lisp and written my very first basic script. Itās adapted from my SET bot Python code, and right now it generates an SVG with a randomly-colored circle.
;;;; sbcl --script setsvg.lisp > foo.svg
;;;; Creates an SVG with a randomly-colored circle.
; Initialize the global random state.
; https://stackoverflow.com/questions/4034042/random-in-common-lisp-not-so-random
(setf *random-state* (make-random-state t))
(defparameter *begin-svg* "<svg xmlns=\"http://www.w3.org/2000/svg\"
xmlns:xlink=\"http://www.w3.org/1999/xlink\"
width=\"500\" height=\"500\">")
(defparameter *end-svg* "</svg>")
(defun ellipse (color)
(concatenate 'string "<ellipse
cx=\"250.0\"
cy=\"250.0\"
rx=\"100.0\"
ry=\"100.0\"
stroke=\"#fc8d62\"
stroke-width=\"5\"
fill=\"#" color "\"
/>")
)
(defparameter *colors* '("66c2a5" "fc8d62" "8da0cb"))
(defun randselect (somelist)
(let
((n (length somelist)))
(car (nthcdr (random n) somelist))
)
)
(defun randcolor ()
(randselect *colors*)
)
(defun svg (body)
(format t "~d" *begin-svg*)
(format t "~d" body)
(format t "~d" *end-svg*)
)
(defun randcircle ()
(svg (ellipse (randcolor)))
)
(randcircle)