✓ Macro Programming¶
S-expression¶
S-expressions¶
The s-expressions are strings that represent lists.
Some examples:
()(1 2 3)(1 (2 3))
The parenthesis marks the boundaries of the lists, and the elements of a list is either
an atom
or another list
The s-expression language¶
Here is the grammar of the s-expression.
s-expr  : (element)*
element : atom
        | s-expr
Atoms can be any of the scalar values:
Numbers
Strings and characters
Symbols
Extension of s-expression in Clojure¶
Clojure extends the basic s-expression by a few more constructs:
Set:
#{ ... }Vector:
[ ... ]Map:
{ ... }
Universal Expressiveness of s-expressions¶
Representation with s-expressions¶
S-expressions can be used to expresss just about anything. So, in a way, s-expression is a core language that can be used to represent any other language.
Programming¶
def factorial(n):
  if n <= 1:
    return n
  else:
    return n * factorial(n-1)
print factorial(100)
(my-program
  (def factorial (n)
    (if (n <= 1)
      (return n)
      (return (n * (call factorial (n - 1))))))
  (call factorial 100))
Markup language¶
<Person id="1234">
  <name>Albert Einstein</name>
  <profession>Physicist</profession>
</Persion>
(person
  (:id "1234")
  (:name "Albert Einstein")
  (:profession "Physicist"))
Stylesheet in CSS¶
body: {
  font-family: Helvetica;
  margin: 10px 0;
  background: transparent;
}
;; This encoding requires that
;; the second element of (body ...) list
;; be a list containing even number of atoms.
(body (font-family "Helvetica"
       margin      (10 0)
       background  :transparent))
Clojure programming¶
Clojure source code is already in s-expression form.
Macro Programming¶
The reader¶
A reader is a program that transforms the native syntax to a s-expression.
Keep in mind that the \(\mathrm{SExpr}\) is to be interpreted by a Lisp runtime environment as a Turing-complete programming environment (such as Clojure).
Magic of Clojure¶
Clojure is homoiconic, so the source code is already in s-expression.
Using Clojure s-expression, we can express arbitrary data transformation.
What if clojure s-expression is the input to itself?
Macro expansion¶
Macros in Clojure¶
Macros are functions¶
(defmacro <function-name>
    [ <arguments>... ]
    <body>)
The arguments are s-expression before.
The return value must be a valid s-expression after.
Walking through¶
(evaluate 1 + 2)
is evaluated as during the macro expansion stage.
The body is evaluated with the scope:
x => 1
op => '+
y => 2
So, the body evaluates to
(+ 1 2)
Example¶
(defmacro evaluate2 [expr]
    (list 'do
          (list 'println (str expr))
          (list 'println "=>" expr)))
#'user/evaluate2
(evaluate2 (+ 1 2 3))
(+ 1 2 3)
=> 6
nil
Walk through¶
(evaluate2 (+ 1 2 3)
creates the following binding during the macro-expansion.
expr => '(+ 1 2 3)
So,
(str expr) => "(+ 1 2 3)"
The returned value is:
(do (println "(+ 1 2 3)")
    (println "=>" (+ 1 2 3)))
Remarks¶
In the macro of
(defmacro evaluate2 [expr]
    (list 'do
          (list 'println (str expr))
          (list 'println "=>" expr)))
We need general computation:
(str expr)to compute the string representation of expr.
We also need code generation:
'do(list ...)
Language features for macro programming¶
Generating code is painful¶
(defmacro inc-10 []
    (list 'let (vector 'x 10) (list clojure.core/inc 'x)))
#'user/inc-10
(macroexpand-1 '(inc-10))
(let [x 10] (#function[clojure.core/inc] x))
(inc-10)
11
Code template¶
Code template is a piece of valid clojure code that is not evaluated by the evaluator.
We can suppress evaluation using the backtick symbol.
All s-expressions in the template are pure data (with exception).
All symbols are resolved into their fully qualified form.
`(let [x 10] (inc x))
(clojure.core/let [user/x 10] (clojure.core/inc user/x))
This allows easy implementation of macros, but more on this later.
Single substitution in code template¶
We can exit from a template and evaluate part of the code inside the template.
`(let [x 10
       y ~(rand-int 1000)]
     (println ~(rand-int 1000) x y))
(clojure.core/let [user/x 10 user/y 827] (clojure.core/println 691 user/x user/y))
Slice substitution in code template¶
We can compute a list and insert it into a code template.
`(let [x 10
       y ~(rand-int 1000)]
     (println ~@(take 3 (repeatedly #(rand-int 1000))) x y))
(clojure.core/let [user/x 10 user/y 414] (clojure.core/println 75 709 326 user/x user/y))
Symbol generation¶
Code templates generates invalid variable names in the let-form.  To fix this, Clojure has a special syntax
that denotes new symbols to be inserted into the generated code.
`(let [x# 10
       y# ~(rand-int 1000)]
     (println ~@(take 3 (repeatedly #(rand-int 1000))) x# y#))
(clojure.core/let [x__5428__auto__ 10 y__5429__auto__ 367] (clojure.core/println 590 319 374 x__5428__auto__ y__5429__auto__))
Examples of macros¶
Echo¶
(defmacro echo [expr]
    (let [message (str expr "=>")]
        `(println ~message ~expr)))
#'user/echo
(echo (+ 1 2))
(+ 1 2)=> 3
nil
Timeit¶
(defmacro timeit [expr]
    `(let [start# (System/currentTimeMillis)
           result# ~expr
           duration# (- (System/currentTimeMillis) start#)]
         (println ~(str expr)
                  "=>"
                  result#)
         (println "Took" duration# "ms")))
#'user/timeit
(timeit (apply + (range 1E7)))
(apply + (range 1.0E7)) => 49999995000000
Took 225 ms
nil
Infix operator¶
(defmacro infix [expr]
    (let [[a op b] expr]
        `(~op ~a ~b)))
#'user/infix
(infix (1 + 2))
3