# ✓ 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

1. an atom

2. 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¶

A reader is a program that transforms the native syntax to a s-expression.

$\mathrm{reader}: \mathrm{Syntax} \to \mathrm{SExpr}$

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¶

$\mathrm{Syntax} \overset{reader}{\longrightarrow} \underbrace{\mathrm{SExpr}}_{\mathrm{before}} \overset{macro}{\longrightarrow} \underbrace{\mathrm{SExpr}}_{\mathrm{after}}$

## 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.

### Example¶

(defmacro evaluate [x op y]
(list op x y))

#'user/evaluate

(evaluate 1 + 2)

3


### 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.

1. All s-expressions in the template are pure data (with exception).

2. 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
`