Blame


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.
6 314cbe1e 2020-06-04 op
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.
10 314cbe1e 2020-06-04 op
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)
12 314cbe1e 2020-06-04 op
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:
15 314cbe1e 2020-06-04 op
16 314cbe1e 2020-06-04 op ```elisp
17 314cbe1e 2020-06-04 op (member (nth 2 (org-heading-components)) org-todo-keywords-1)
18 314cbe1e 2020-06-04 op ```
19 314cbe1e 2020-06-04 op
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...)
22 314cbe1e 2020-06-04 op
23 314cbe1e 2020-06-04 op In a hypothetical elisp-clojure mix that line can be written as:
24 314cbe1e 2020-06-04 op ```clojure
25 314cbe1e 2020-06-04 op (->> (org-heading-components)
26 314cbe1e 2020-06-04 op (nth 2)
27 314cbe1e 2020-06-04 op (-> (member org-todo-keywords-1)))
28 314cbe1e 2020-06-04 op ```
29 314cbe1e 2020-06-04 op
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
33 314cbe1e 2020-06-04 op readable.
34 314cbe1e 2020-06-04 op
35 314cbe1e 2020-06-04 op If you don't know what the `->` macro in clojure does, it does this:
36 314cbe1e 2020-06-04 op ```clojure
37 314cbe1e 2020-06-04 op (-> x
38 314cbe1e 2020-06-04 op (* 5)
39 314cbe1e 2020-06-04 op (/ 2)
40 314cbe1e 2020-06-04 op inc)
41 314cbe1e 2020-06-04 op
42 314cbe1e 2020-06-04 op ;; gets rewritten as
43 314cbe1e 2020-06-04 op
44 314cbe1e 2020-06-04 op (inc (/ (* x 5) 2))
45 314cbe1e 2020-06-04 op ```
46 314cbe1e 2020-06-04 op
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.
50 314cbe1e 2020-06-04 op
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.
56 314cbe1e 2020-06-04 op
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)
59 314cbe1e 2020-06-04 op
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.
65 314cbe1e 2020-06-04 op
66 314cbe1e 2020-06-04 op ```elisp
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))
70 314cbe1e 2020-06-04 op (cond
71 314cbe1e 2020-06-04 op ((eq xs nil)
72 314cbe1e 2020-06-04 op x)
73 314cbe1e 2020-06-04 op
74 314cbe1e 2020-06-04 op ((symbolp (car xs))
75 314cbe1e 2020-06-04 op `(-> (,(car xs) ,x)
76 314cbe1e 2020-06-04 op ,@(cdr xs)))
77 314cbe1e 2020-06-04 op
78 314cbe1e 2020-06-04 op (t
79 314cbe1e 2020-06-04 op `(-> (,(caar xs) ,x ,@(cdar xs))
80 314cbe1e 2020-06-04 op ,@(cdr xs)))))
81 314cbe1e 2020-06-04 op ```
82 314cbe1e 2020-06-04 op
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?
85 314cbe1e 2020-06-04 op
86 314cbe1e 2020-06-04 op Then we have the two recursive cases:
87 314cbe1e 2020-06-04 op
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 ...) ...)`.
90 314cbe1e 2020-06-04 op
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).
94 314cbe1e 2020-06-04 op
95 314cbe1e 2020-06-04 op The counterpart, `->>`, is almost identical in its implementation:
96 314cbe1e 2020-06-04 op
97 314cbe1e 2020-06-04 op ```elisp
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))
101 314cbe1e 2020-06-04 op (cond
102 314cbe1e 2020-06-04 op ((eq xs nil)
103 314cbe1e 2020-06-04 op x)
104 314cbe1e 2020-06-04 op
105 314cbe1e 2020-06-04 op ((symbolp (car xs))
106 314cbe1e 2020-06-04 op `(->> (,(car xs) ,x)
107 314cbe1e 2020-06-04 op ,@(cdr xs)))
108 314cbe1e 2020-06-04 op
109 314cbe1e 2020-06-04 op (t
110 314cbe1e 2020-06-04 op `(->> (,@(car xs) ,x)
111 314cbe1e 2020-06-04 op ,@(cdr xs)))))
112 314cbe1e 2020-06-04 op ```