Macros in Emacs Lisp
Compare and symbols
Compare functions in programming languages are commonly three-way, for equality, greater than, and smaller than. The return value is expressed as an int, e.g. -1, 0, 1. But you could also return a symbol:
(require 'cl-lib) (defun compare (x y) (cond ((< x y) '<) ((> x y) '>) (t '=)))
For example:
(dolist (v '((1 2) (2 1) (2 2) (0 10000))) (cl-destructuring-bind (x y) v (hly/debug-funcall (compare x y))))
(compare 1 2): < (compare 2 1): > (compare 2 2): = (compare 0 10000): <
You could use this in a pattern matcher:
(cl-case (compare 3 54) ('< (message "It's smaller")) ('> (message "It's larger")) ('= (message "It's equal")))
It’s smaller
Macros, Emacs Lisp style
But, really, that just invites its own macro:
(defmacro mcompare (a b &rest forms) (org-with-gensyms (res) (cl-flet ((quote-first-elem ((sym &rest body)) (cons `',sym body))) `(let ((,res (compare ,a ,b))) (cl-case ,res ,@(mapcar #'quote-first-elem forms) (otherwise (error "Unexpected comparison result: %s" ,res)))))))
Yuck!
You can use this macro as follows:
(mcompare 3 54 (< (message "It's smaller")) (> (message "It's larger")) (= (message "It's equal")))
It’s smaller
And this throws an error:
(condition-case err (mcompare 3 54 (> (message "It's larger")) (= (message "It's equal"))) (error (message "Got error: %S" err)))
Got error: (error "Unexpected comparison result: <")
Macros, Scheme style
Since we’re on the topic of macros.. those macros look ugly. Let’s try Scheme style macros, aka “MBE”, “Macros By Example”, or “SRFI 46”:
(require 'mbe) (mbe-defrules hcompare ((a b (s body ...) ...) (let ((res (compare a b))) (cl-case res ('s body ...) ... (otherwise (error "Unexpected comparison result: %s" res))))))
So clean!
Does it work?
(hcompare 300 54 (< (message "It's smaller")) (> (message "It's larger")) (= (message "It's equal")))
It’s larger
Yes.
However…
Hygiene
(let ((res 10)) (hcompare 300 54 (< (message "It's smaller, and res is `%s' (should be 10)" res)) (> (message "It's larger, and res is `%s' (should be 10)" res)) (= (message "It's equal, and res is `%s' (should be 10)" res))))
It’s larger, and res is ‘>’ (should be 10)
That’s not good: the macro overrides the value of res
with its own internal value.
Unfortunately this implementation of SRFI 46 in Emacs Lisp is not hygienic (as it states on the README of that project). Problem is that I’m not sure how to make it so, because the MBE syntax means you can’t easily drop out of the macro…
Org mode help and links
Internet resources I used to set up this document:
- https://www.reddit.com/r/emacs/comments/7v0q3y/how_can_i_evaluate_orgbabel_code_without/
- https://emacs.stackexchange.com/questions/23946/how-can-i-stop-the-confirmation-to-evaluate-source-code-when-exporting-to-html
- https://org-babel.readthedocs.io/en/latest/header-args/
- https://github.com/ijp/mbe.el
- https://emacs.stackexchange.com/questions/14031/name-of-emacs-lisp-library-that-provides-with-gensyms-classic-macro
- https://emacs.stackexchange.com/questions/27776/case-doesnt-switch-correctly-with-strings
- https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html
- https://emacs.stackexchange.com/questions/46020/using-file-local-variables-in-org-mode
- https://emacs.stackexchange.com/questions/38004/how-to-display-both-output-and-value-results-in-org-mode-for-a-source-code-block