# Macros¶

Note

These slides are also available in PDF format: 4:3 PDF, 16:9 PDF, 16:10 PDF.

## Background on Symbolic Computation¶

• Wikipedia considers symbolic computation to be simply computer algebra.
• While computer algebra is a form of symbolic computation, there are plenty of other applications.
• Programming languages
• Compilers
• Artificial intelligence

## Lisp & Symbolic Computation¶

Lisp dialects have a homoiconic syntax: the code is data, and data is code. Lists being the structure of the language syntax, code can be manipulated just like lists.

• This is what (historically) gave LISP its name (LISt Processor)
• The concept of “quoting” is fairly unique to just Lisp.
• It leads to a natural way to manipulate and work on code in the language.
• Key point: we can manipulate code before it is evaluated!

John McCarthy (1958)

Recursive Functions of Symbolic Expressions and their Computation by Machine

## Motivation¶

• Even in the best software designs, it’s hard to avoid repetitive patterns.
• What if our language let us extend its syntax to account for these patterns?

Exercise for Home

Find a piece of code you wrote (in any language) which repeats a syntax pattern you couldn’t avoid by writing a function, class, etc.

## What do I mean by “extend syntax”?¶

We can implement most all of the functionality we need in Python using functions. But can we implement something like Racket’s let in Python?

let (x = 10,
y = 20) in:
print(x, y)


(Python does not support above)

## How about C Macros?¶

The C Preprocessor lets us do simple text substitutions:

#define FOREVER for (;;)

main () {
FOREVER {
printf("Hello, World!\n");
}
}


(they can get a little more complicated than that…)

But what happens when we want to do more complex things? Like manipulate the body of that “FOREVER loop”?

## C Macros¶

At some point, textual source manipulation cannot serve the purpose we need anymore. Let this source from MicroPython serve as an example:

STATIC mp_obj_t machine_spi_init(...) {
...
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);

STATIC mp_obj_t machine_spi_deinit(...) {
...
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit);

STATIC mp_obj_t mp_machine_spi_read(...) {
...
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read);

STATIC mp_obj_t mp_machine_spi_readinto(...) {
...
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto);


## Lisp Macros¶

Hopefully it’s become apparent that symbolic computation is the right tool for the job when it comes to macros.

Lisp Macros:

• Compile time
• Syntax $$\to$$ Syntax

Lisp Functions:

• Run time
• Data $$\to$$ Data
• Lisp dialects usually make the run time available during the compile time, so the normal language can be used to write macros.

## Old-School Lisp Macros¶

Early Lisp macro systems operated on the simple contract of functions which take syntax, manipulate it, and returns a list containing the new syntax:

(defmacro repeat-forever (&rest body)
(prog ()
a   ,@body
(go a)))

;; we can then use the macro like this:
(repeat-forever
(format t "HELLO WORLD~%"))


## Old-School: More Examples¶

“let” as a macro:

(defmacro let (bindings &rest body)
((lambda ,(mapcar #'car bindings)
,@body)
,@(mapcar #'cadr bindings)))

;; we can then use let like this:
(let ((a 10)
(b 20))
(format t "~A ~A~%" a b))


## Old-School: Another Example¶

Suppose we wanted to define a syntax like this:

(numeric-case num
negative
zero
positive)


We could write a macro like this:

(defmacro numeric-case (num negative zero positive)
(let ((result ,num))
(cond
((< result 0) ,negative)
((= result 0) ,zero)
(t ,positive))))


What could possibly go wrong?

## Fixing numeric-case with gensym¶

gensym is here to save us when we need really obscure symbol names:

(defmacro numeric-case (num negative zero positive)
(let ((sym (gensym)))
(let ((,sym ,num))
(cond
((< ,sym 0) ,negative)
((= ,sym 0) ,zero)
(t ,positive)))))


## More Macro Issues¶

• What happens if the programmer redefined one of the functions we used (e.g., < or =) in the previous example?

Unhygienic Macros

Modern Lisp dialects typically provide what is called hygienic macros: macro systems which eliminate the issues we discovered with old-school Lisp macros (to varying degrees)

## Racket’s Hygienic Macros¶

• define-syntax defines compile-time syntax: a function that takes a “syntax” and returns a “syntax”.
• Typical syntax operations provide a convenient way to manipulate the syntax in a hygenic manner.
• You can also go unhygienic: syntax->datum converts syntax to lists, symbols, etc., and datum->syntax goes back.

## What is a “syntax”?¶

Syntax literals can be written using #' :

> #'(if (> 0 x) y z)
#<syntax:readline-input:1:2 (if (> 0 x) y z)>
> (define stx #'(if (> 0 x) y z))


We can convert this to a list if we wish:

> (syntax->datum stx)
'(if (> 0 x) y z)


And back:

> (datum->syntax stx (syntax->datum stx))
#<syntax (if (> 0 x) y z)>


If you didn’t have access to the original syntax object, you could pass #f as the first argument to datum->syntax.

  Note this is completely different from the function-namespace thing in old-school Lisps.

## Going Unhygienic¶

We could write our let macro without considerations for hygiene:

(define-syntax (my-let stx)
(datum->syntax
stx
(let ([stx-list (syntax->datum stx)])
((lambda ,(map car (cadr stx-list))
,@(cddr stx-list))
,@(map cadr (cadr stx-list))))))


A little bit yucky, but it worked.

## Doing Things Hygienic¶

syntax-case acts like match but for syntax objects:

(define-syntax (my-let stx)
(syntax-case stx ()
[(_ ([name expr] ...) body ...)
#'((lambda (name ...)
body ...)
expr ...)]))


## define-syntax-rule Shorthand¶

define-syntax-rule is a shorthand for a define-syntax with a syntax-case of a single rule inside.

(define-syntax-rule (my-let ([name expr] ...) body ...)
((lambda (name ...)
body ...)
expr ...))


## Application of Macros: Anaphora¶

In natural language, anaphora is a reference to a previously defined noun:

Susan dropped $$\underbrace{\text{the plate}}_{\text{referent}}$$. $$\underbrace{\text{It}}_{\text{anaphor}}$$ shattered loudly!

Lisp programmers call a similar technique the same name:

(printf "~a~%"
(aif (member 10 lst)
it
"10 not in the list"))


Available in a Racket Package

The “anaphoric” package provides aif, awhen, acond, and aand.

## Anaphoric If¶

Example from Fear of Macros:

(require racket/stxparam)

(define-syntax-parameter it
(lambda (stx)
(raise-syntax-error (syntax-e stx) "outside of anaphora")))

(define-syntax-rule (aif predicate consequent alternative)
(let ([result predicate])
(if result
(syntax-parameterize ([it (make-rename-transformer #'result)])
consequent)
alternative)))
`