Why is the macro systems in lisps considered so valuable


#21

Can you elaborate?


#22

see:

(def defmacro (fn [&form &env name & args]))

#23

Yes, but notice this line:

(. (var defmacro) (setMacro))

Which will notify the compiler to treat defmacro as a Macro, and therefore it will be macro-expanded at compile-time (with no runtime penalty).

More info:


#24

There are many surprising uses to macros. One of them is splitting code. Imagine

(let [that (we-have)
      several (elements)
      of (computation)
      layed-out (as such)
      in (a let)]
  ...)

Let’s suppose we also want to perform a series of operations tangentially to the sequence of computations above, for instance to do some logging. We might end up with something like this.

(let [that (we-have)
      _ (log that)
      several (elements)
      _ (log several)
      of (computation)
      _ (log of)
      layed-out (as such)
      _ (log layed-out)
      in (a let)
      _ (log in)]
  ...)

To preserve readability by separating orthogonal sides of code we can manage something like this.

(defmacro with-logging [[_let bindings]
  `(let ~(add-logging bindings)))

(with-logging
  (let [that (we-have)
        several (elements)
        of (computation)
        layed-out (as such)
        in (a let)]
    ...)))

What’s nice is that instead of refactoring the initial let into a new abstraction like functions that compose monadically, I can stick to my code and let the refactoring be done by a macro in terms common to all these competing abstractions: in Clojure like in all lisps, inert code is just nested lists of symbols.

Also they can be very short and powerful. I wrote this one out of frustration fighting multiple levels of ifs to cohabit with a few lets and decided to flatten it all.

;; Goal: a macro similar to `let` that evaluates its bindings lazily.

(require '[clojure.tools.macro :refer [symbol-macrolet]])

(defmacro lay [[sym expr & more-bindings] & body]
  (let [delay-sym (gensym (str "laid-" sym "-"))]
    `(let [~delay-sym (delay ~expr)]
       (symbol-macrolet [~sym (deref ~delay-sym)]
         [email protected](if (empty? more-bindings)
             body
             `[(lay ~more-bindings [email protected])])))))

;; Simple example
(defmacro lay-it [& body]
  `(lay [~'a (print "a")
         ~'b (print "b")
         ~'c (do ~'b (print "c"))]
     [email protected]
     (newline)))

(lay-it a)     ;; a
(lay-it b)     ;; b
(lay-it a b)   ;; ab
(lay-it b a)   ;; ba
(lay-it c)     ;; bc
(lay-it c a)   ;; bca
(lay-it c b a) ;; bca (no re-evaluation, uses cached result instead)