Skip to content

Instantly share code, notes, and snippets.

@Wilfred
Last active August 29, 2015 14:08
Show Gist options
  • Save Wilfred/d8fe7072053315bfd92a to your computer and use it in GitHub Desktop.
Save Wilfred/d8fe7072053315bfd92a to your computer and use it in GitHub Desktop.

Macros Crash Course

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment