1 314cbe1e 2020-06-04 op Clojure was the first lisp I've learned. Well, technically speaking,
2 314cbe1e 2020-06-04 op my first one was scheme (racket actually), because was the language
3 314cbe1e 2020-06-04 op chosen as an introduction to programming in my university. Back in
4 314cbe1e 2020-06-04 op the days, I was way more stupid than I am now (or at least I like to
5 314cbe1e 2020-06-04 op think that) and I didn't really liked the language at all.
7 314cbe1e 2020-06-04 op Fortunately enough, a couple of months later, I don't remember why, I
8 314cbe1e 2020-06-04 op started learning clojure. I found the idea of code-as-data, the sexp
9 314cbe1e 2020-06-04 op thing, the syntax was... interesting.
11 314cbe1e 2020-06-04 op (don't hate on me: recently I given scheme a second chance and I heavily respect it. It's really a clean, nice and elegant language)
13 314cbe1e 2020-06-04 op Fast forward several years: (as a complete elisp noob) I'm hacking a
14 314cbe1e 2020-06-04 op piece of my Emacs configuration. I have a line like this:
17 314cbe1e 2020-06-04 op (member (nth 2 (org-heading-components)) org-todo-keywords-1)
20 314cbe1e 2020-06-04 op (now that I think of it, this is not a great example to show of how
21 314cbe1e 2020-06-04 op cool is the `->` macro, but still...)
23 314cbe1e 2020-06-04 op In a hypothetical elisp-clojure mix that line can be written as:
25 314cbe1e 2020-06-04 op (->> (org-heading-components)
27 314cbe1e 2020-06-04 op (-> (member org-todo-keywords-1)))
30 314cbe1e 2020-06-04 op Sometimes some lispy form tends to be written *backwards* due to the
31 314cbe1e 2020-06-04 op syntax of the language. The threading macro lets you *invert* the
32 314cbe1e 2020-06-04 op flow of the expression, and sometimes this makes the whole more
35 314cbe1e 2020-06-04 op If you don't know what the `->` macro in clojure does, it does this:
42 314cbe1e 2020-06-04 op ;; gets rewritten as
44 314cbe1e 2020-06-04 op (inc (/ (* x 5) 2))
47 314cbe1e 2020-06-04 op That is, the first parameter is passed as first argument on the next
48 314cbe1e 2020-06-04 op form, and so on. The `->>` is similar, but adds the parameter as
49 314cbe1e 2020-06-04 op *last* argument.
51 314cbe1e 2020-06-04 op If you haven't seen anything about how macros are written, well, I
52 314cbe1e 2020-06-04 op don't think I'm the most qualified to show how they works, but the
53 314cbe1e 2020-06-04 op idea is that a macro gets called at compile time, when the compiler
54 314cbe1e 2020-06-04 op has just finished reading the whole expression. The macro is
55 314cbe1e 2020-06-04 op therefore a function executed at compile time that returns code.
57 314cbe1e 2020-06-04 op (these kind of macros are not the only possible. Some lisp dialects
58 314cbe1e 2020-06-04 op allows *reader macro*s, but that's another story)
60 314cbe1e 2020-06-04 op What follows is an amateurish implementation of the macro in elisp.
61 314cbe1e 2020-06-04 op It is definitely not something hard to write (quite the opposite in
62 314cbe1e 2020-06-04 op fact) but can be useful as some sort of introduction to macros in
63 314cbe1e 2020-06-04 op elisp, to show how *malleable* lisps are and to highlight how clean
64 314cbe1e 2020-06-04 op the code is thanks to a little recursion.
67 314cbe1e 2020-06-04 op (defmacro -> (x &rest xs)
68 314cbe1e 2020-06-04 op "Should work like clojure `->'."
69 314cbe1e 2020-06-04 op (declare (indent defun))
74 314cbe1e 2020-06-04 op ((symbolp (car xs))
75 314cbe1e 2020-06-04 op `(-> (,(car xs) ,x)
79 314cbe1e 2020-06-04 op `(-> (,(caar xs) ,x ,@(cdar xs))
80 314cbe1e 2020-06-04 op ,@(cdr xs)))))
83 314cbe1e 2020-06-04 op First we have our base case: if `xs` is nil (i.e. the case of `(->
84 314cbe1e 2020-06-04 op x)`) we can rewrite it as `x`. Easy, right?
86 314cbe1e 2020-06-04 op Then we have the two recursive cases:
88 314cbe1e 2020-06-04 op - `(-> x fn ...)` gets rewritten as `(-> (fn x) ...)` and
89 314cbe1e 2020-06-04 op - `(-> x (fn ...) ...)` gets rewritten as `(-> (fn x ...) ...)`.
91 314cbe1e 2020-06-04 op The revelation writing this macro was that when I first learn of
92 314cbe1e 2020-06-04 op `caar`, `cadr`, `cdar`, `caadr`, ... I thought of them being a joke
93 314cbe1e 2020-06-04 op while now I can see that they can lead to cleaner code (sort of).
95 314cbe1e 2020-06-04 op The counterpart, `->>`, is almost identical in its implementation:
98 314cbe1e 2020-06-04 op (defmacro ->> (x &rest xs)
99 314cbe1e 2020-06-04 op "Should work like clojure' `->>'"
100 314cbe1e 2020-06-04 op (declare (indent defun))
105 314cbe1e 2020-06-04 op ((symbolp (car xs))
106 314cbe1e 2020-06-04 op `(->> (,(car xs) ,x)
110 314cbe1e 2020-06-04 op `(->> (,@(car xs) ,x)
111 314cbe1e 2020-06-04 op ,@(cdr xs)))))