#'clojure.core/some-> and the Power of Macros

Posted on 2 January, 2015 by Eric Bailey
Tags: clojure, lisp, metaprogramming

Homoiconicity, and metaprogramming, and macros! Oh my!

In 2015, my love for these elegant weapons will undoubtedly grow fonder.

(comment
  ;; #'clojure.core/some-> is one my favorite Clojure macros.
  (some-> (meta #'clojure.core/some->) :doc println)
  ;; When expr is not nil, threads it into the first form (via ->),
  ;; and when that result is not nil, through the next etc

  (macroexpand '(some-> (meta #'clojure.core/some->) :doc println))

  (let* [G__2986 (meta (var clojure.core/some->))
         G__2986 (if (clojure.core/nil? G__2986)
                   nil
                   (clojure.core/-> G__2986 :doc))
         G__2986 (if (clojure.core/nil? G__2986)
                   nil
                   (clojure.core/-> G__2986 println))]
    G__2986)

  ;; Rewritten more readably to illustrate the point:
  (let [example (meta #'clojure.core/some->)
        example (when-not (nil? example)
                  (:doc example))
        example (when-not (nil? example)
                  (println example))]
    example)

  ;; Which brings me to another of my scattered thoughts,
  ;; Clojure has the #'clojure.core/when-not macro, but
  ;; for some reason, I find myself wanting to use 'unless,
  ;; as in Common Lisp:
  (defmacro unless (condition &rest body)
    `(if (not ,condition) (progn ,@body)))

  ;; What better excuse to start writing my own macros?
  (ns fastidious.macros)

  (defmacro unless
    [condition & body] `(if (not ~condition) (do ~@body)))

  ;; Side bonus: I used my desire for 'unless to drive
  ;; my experiments in integrating macros in ClojureScript,
  ;; with underwhelming success. It just works.
  (ns client.core
    (:require-macros [fastidious.macros :refer [unless]]))

  (let [lisp-is-lame? false]
    (unless lisp-is-lame?
      (println "IFL Lisp.")
      (println "Next up: /c[ad]{1,5}r/"))))

See also: #'clojure.core/some-> source on GitHub