Firstly, a macro is evaluated at compile-time, not runtime.
;; Define a function and macro that both
;; return the current time.
(defun get-current-time-fn ()
(second (current-time)))
(defmacro get-current-time-mac ()
(second (current-time)))
(defun use-get-current-time-fn ()
(get-current-time-fn))
(defun use-get-current-time-mac ()
(get-current-time-mac))
;; (use-get-current-time-fn) ; different every time.
;; (use-get-current-time-fn)
;; (use-get-current-time-mac) ; same every time
;; (use-get-current-time-mac)
This is useful for implementing control flow that we can't express as a simple function.
(defvar ice-creams 0)
(defun user-is-happy-p ()
(> ice-creams 3))
;; We want to write
(until (user-is-happy-p)
(incf ice-creams))
;; Instead of:
(while (not (user-is-happy-p))
(incf ice-creams))
;; We can build this list:
(defmacro until (condition &rest body)
(append (list 'while (list 'not condition)) body))
Lisps provide a convenient way of building lists from a template. It's
a bit like sprintf formatting, e.g. printf("x is %d", x");
.
(defmacro until (condition &rest body)
;; , substitutes the value in (for clojure, use ~)
;; ,@ substitutes the value in and appends it to
;; the current list (for clojure, use ~@)
`(while (not ,condition) ,@body))
Macros are tricky to debug. Use macroexpand
to check you're getting
what you expected.
(macroexpand
'(until (user-is-happy-p) (incf ice-creams)))
;; Gives: (while (not (user-is-happy-p)) (incf ice-creams))
However, calling macroexpand
by hand is tedious. Install
macrostep.el
and you can interactively see how macros expand (for
Clojure there's cider-macroexpansion.el
).
Give it a try yourself: try to implement my-and
as a macro that acts
as and
. You only need if
and recursion.