Blob


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