In this tutorial, we'll discuss the construction of programs at
*compile time* using what are called *macros*.

Some Racket forms like `and`

and `or`

look like function calls, but we
know they are not because they support short-circuit evaluation. We
can define forms that behave differently than function calls (or that
look like function calls, but actually do other things as well, like
the `rackunit`

`check-`

forms). These forms are sometimes called
*macros*.

# Redefining `and`

The simplest way of defining a macro in racket is to use define-syntax-rule. As a first approximation, you can think of these syntax rules as functions that don't evaluate their arguments.

Let's start by defining

`And`

, which is just like the lower case version except the short-circuit evaluation is from the right.

```
#lang racket
(define-syntax-rule (And a b)
(if b a #f))
(module+ test
(require rackunit)
(define (die)
(error 'die "don't run this"))
(check-equal? (And (die) #f) #f)
(check-exn exn:fail? (lambda () (and (die) #f))))
```

- Notice the use of
`lambda`

to delay evaluation in`check-exn`

; that's exactly the kind of boilerplate we can eliminate with a syntax rule:

```
(module+ test
(define-syntax-rule (check-fail expr)
(check-exn exn:fail? (lambda () expr)))
(check-fail (and (die) #f))
(check-fail (And #f (die))))
```

- Macros are a key part of the implimentation strategy of racket. What's an example of some syntax we saw already that is (probably) implimented as a macro?

# Redefining `or`

- Let's define
`Or`

in terms of`if`

in a similar way to`And`

. It should pass the following tests

```
(module+ test
(check-equal? (Or #t #t) #t)
(check-equal? (Or #f #t) #t)
(check-equal? (Or #t #f) #t)
(check-equal? (Or (die) #t) #t)
(check-fail (or (die) #t)))
```

## Let and Let*

In homework 3 we implimented an evalator for `with*`

using `with`

.
It turns out that `let*`

can be implimented in a similar way in terms
of `let`

using a macro.

Let's start by reviewing `let*`

:

```
(module+ test
(check-equal? (let* ([x 5]
[y (- x 3)])
(+ y y))
4)
(check-equal? (let* ([x 5]
[y (- x 3)]
[y x])
(* y y))
25))
```

- The key insight is that
`let*`

behaves like a nested set of`let`

s. Let us transform the examples about according to this idea:

```
(module+ test
(check-equal? (let ([x 5])
(let ([y (- x 3)])
(+ y y)))
4)
(check-equal? (let ([x 5])
(let ([y (- x 3)])
(let ([y x])
(* y y))))
25))
```

That's more cumbersome to write, of course, but that's why

`let*`

exists. So how can we formulate this recursively? Well, racket syntax already looks like a list, so lets turn this into a recursive problem on lists. Start a new file`let-transformer.rkt`

Complete the following skeleton for the

`let-transformer`

module.

```
(provide let-transformer)
(define (let-transformer lst)
(match lst
[(list 'Let* '() body) ]
[(list 'Let* (cons (list ) tail) body)
(list 'let (list (list id val))
(let-transformer
(list 'Let* )))]))
(module+ test
(require rackunit)
(check-equal? (let-transformer '(Let* ([x 5]
[y (- x 3)])
(+ y y)))
'(let ([x 5]) (let ([y (- x 3)]) (+ y y)))))
```

# From list transformers to macros.

- Prerequisites
- match

In this last part we'll show how to extend the Racket Compiler in a quite general way.

Start a new file

`let-macros.rkt`

. Roughly speaking, the following takes the input racket code`stx`

, turns it into a list, processes with the function we defined already, and then turns it back into racket code.

```
(require (for-syntax racket/match))
(require (for-syntax "let-transformer.rkt"))
(define-syntax (Let* stx)
(datum->syntax #'here (let-transformer (syntax->datum stx))))
```

- We can also use
`syntax-case`

, which is roughly speaking`match`

for macros. This eliminates the need to write a seperate transformer function. It's also harder to debug. (`Let^`

is just the macro name, so as not to collide with`Let*`

)

```
(define-syntax (Let^ stx)
(syntax-case stx ()
[(Let^ () body) #'body]
[(Let^ ((first-id first-val) (id val) ...) body)
#'(let ([first-id first-val])
(Let^ [(id val) ...] body))]))
(module+ test
(require rackunit)
(check-equal? (Let^ ([x 5] [y (- x 3)]) (+ y y)) 4)
(check-equal? (Let^ ([x 5] [y (- x 3)] [y x]) (* y y)) 25))
```