Today I picked up where I left off in Chapter 4 of Practical Common Lisp. I began by reading the sections about macros. I'm not really sure I understand them that well yet. All I understand is that they allow you to extend CL's syntax to be better suited for your purposes (awesome concept) and they somehow provide a hook into the compiler. Chapters 7 and 8 are dedicated to macros though so I'm sure that I'll better understand them then.
I also ready about how equality is checked in Common Lisp. It turns out that there are four "generic" equality predicates built in to the language. This is weird to me since the most I've ever seen has been JavaScript with two "generic" equality predicates (==
and ===
). But here there are eq
, eql
, equal
, and equalp
. One thing I think I can say about eq
is that I want to stay away from it like the plague. What it does depends on which implementation of CL you're using. And it seems that you should never feed numbers or characters into it. It just sounds awful. eql
sounds far better.
One redeeming quality as the author notes though is "If these predicates don't suit your needs, you can always define your own predicate function that compares different types of objects in the way you need." This is relieving because, if I really want to, I can just define my own equality predicate that behaves exactly how I'm used to it in other languages.
Seibel also went into how to format Lisp code. Most of it was stuff I had already heard from people on Twitter and GitHub as well as some brief code I skimmed through on the web. The formatting rules make sense. I still think all those trailing parentheses will take some getting used to though. I'm accustomed to opening and then closing a parenthese immediately and then going back in between them and writing whatever code I need to write.
Some people recommended to me on Twitter that I use Emacs with SLIME for all my Lisp writing as opposed to just using them for my REPL work and using VS Code to write Lisp files. I'm gonna do this from now on, I just don't want to. In the short-term, it'll be a pain in the ass but I know it'll make my Lisp-hacking more productive in the future.
Also, I want to reflect a bit on Michael Herda's comment I saw yesterday. Here it is for reference:
Many things in Lisp are lists - including forms that represent function calls, for example.
A form is a thing that is meant to be evaluated. In other words, something that isn't meant to be evaluated cannot be a form.
A form does not have to be a list. For example, a form such as
"Hello!"
will evaluate to itself - a string, while the form*random-state*
- which is a symbol - will evaluate to the value bound to that symbol.In other words, you can ask two questions about Lisp objects - what type are they? (list, string, symbol, ...), and, are they meant to be evaluated? (form or not).
(+ 1 2 3)
can be read in two ways - either as code or as data. Reading it as code, it is a form that evaluates a function call to#'+
with three arguments. Reading it as data, it is a list with four elements: the symbol+
and the integers1
,2
, and3
.
I think it's just so elegant that Lisp objects can be interpreted as either data or code. That's just brilliant. It makes me wonder why all languages haven't done this since Lisp was invented. My knowledge of the reasons why programming languages are the way they are (both the historical and technical reasons) is fairly limited compared to many other comp sci people. It's not something I'm going to go into much depth on right now but, if anyone reading this wants to explain it to me why not all languages treat code and data as interchangeable, go ahead.