# CS 536 Homework 1: Macros

### Due: September 15 in class (hardcopy)

If you already know basic Scheme programming, expand your skills by learning how to use macros. We won't cover macros in class, but here are some examples and sources to look at to get started:

• Paul Graham's On Lisp provides an excellent and readable introduction to macros, what they can and should do, and how to think about them. His book uses different macro syntax than what I explain below, but gives a wonderful intro nonetheless. The macro material starts in chapter 7.

• Search for "macros" in the DrScheme HelpDesk. This brings up far more information than you need (and likely more than you can process if you are new to macros), but it is there for reference.

If you have a macro question while working on this assignment, don't hesitate to ask. I'm not an expert on Scheme macros, but I should be able to help with whatever you'll encounter on this assignment.

### Basics of Scheme Macros

Macros differ from functions in that evaluating a function yields a value, wihle evaluating a macro yields an expression. Macros are useful for many reasons. Among them, they let you introduce new syntax into your programs, and they you alter how Scheme would otherwise evaluate an expression if it were a function call instead of a macro.

The `or` operator provides a classic example of the latter. Consider the expression `(or true (+ 4 'a))`. As `or` short-circuits in Scheme, this program should return true. Assume we wrote `or` as a function:

```(define (or e1 e2)
(if e1 true e2))
```

Then evaluating `(or true (+ 4 'a))` would yield an error because Scheme evaluates its arguments before evaluating the body of the function. Thus, we cannot implement `or` with a function. We could, however, implement it as a macro using the following code:

```(define-syntax my-or
(syntax-rules ()
[(my-or e1 e2)
(if e1 true e2)]))
```

Now, evaluating `(my-or true (+ 4 'a))` returns true, rather than an error, as it should.

The general form of a Scheme macro is:

```(define-syntax macro-name
(syntax-rules ([concrete-literals])
[input-pattern
output-pattern]
...))
```

The concrete-literals indicate parts of the syntax that should be matched exactly, rather than binding to some other expression. For example, if I wanted to be able to write more verbose if-statements such as `(my-if (= 5 6) then 2 else 3)` (where then and else are keywords), I could define the following macro:

```(define-syntax my-if
(syntax-rules (then else)
[[my-if test then e1 else e2]
(if test e1 e2)]))
```

The above example illustrates how we can add new syntax to Scheme using macros.

The two examples I've shown you so far illustrate forms where the number of items in the macro expression is fixed. In practice, we often wish to write macros where the syntax follows a pattern, but the number of instances of that pattern is not fixed. A good example of this is Scheme's `let` construct:

```(let ([x 4]
[y 9])
(+ x y))
```

`let` introduces local variables. You can specify as many let variables as you want, then use them in the single expression in the body of the `let`. `Let` is really equivalent to a lambda expression. For example, the following expression implements the above `let` expression:

```((lambda (x y) (+ x y))
4 9)
```

Here is a macro for let. To handle the arbitrary number of variables, we use ellipses after the first use of the pattern `[var val]`. In the output pattern, we use the ellipses again to say that all items in var position should become parameters to the lambda, and all items in val position should become arguments in the function call.

```(define-syntax my-let
(syntax-rules ()
[(let ([var val] ...) body)
((lambda (var ...)
body)
val ...)]))
```

### Exercises

1. Consider an operator `time` that takes a single expression as an argument and returns the time (in seconds) that the expression took to run (assume you have an operator (current-seconds) to give you the current time). Should you implement `time` as a function or a macro? Justify your answer (in a sentence or two) and provide code for an implementation.

2. Write a macro for for-loops. Your macro should support the following expression format:

`(for i 1 5 do (printf "~a~n" i))`

When run, this example should print the numbers 1 through 5, each number on its own line.

Hint: look up the Scheme form `letrec` in HelpDesk.

3. Above, I gave you a macro for binary `or` expressions. Write a macro for `or` expressions that take an arbitrary number of arguments (including 0). The arguments should be evaluated left to right, and the expression should return true as soon as it encounters an expression that evaluates to true.

Examples:
`(multi-or) = false`
`(multi-or (= 3 5) (> 3 1) (+ 'a 4)) = true`
`(multi-or (= 3 5) (< 3 1) (+ 'a 4)) = an error`

4. The `let` form I showed above does not allow one variable in a single let clause to refer to another. For example, the code

```(let ([x 5]
[y (+ x 1)])
(+ x y))
```

Would yield an error that `x` is an unbound identifier (from the lambda definition, it should be clear why this happens). There are times, however, when we would like to write let-style expressions that allow each local var to refer to ones defined before it. This form is called `let*` in Scheme:

```(let* ([x 5]
[y (+ x 1)])
(+ x y))
yields 11
```

Develop a macro for `my-let*` (same as `let*`, but that name is already in use). Don't use letrec, as that is more powerful than let*.

5. Suppose you work for a hardware design firm and have been asked to develop a simulator for state machines. You need to provide a way to specify state machines and to run them on a list of input characters. A run should return a boolean indicating whether the sequence of input characters corresponds to a valid run through the machine. (An invalid run would be on in which there was no transition specified for some input symbol).

1. Implement the state machine simulator without using macros. You'll need to define a representation for state machines and a way to run them on a sequence of input symbols. The result of your run should be a boolean as described above.

2. Implement the state machine simulator using macros. As an (obligatory traffic light) example, here's a proposed syntax for state machines and how you might run it (the machine expects that input symbols occur in the pattern (red* green* yellow*)*):

```(define traffic-check
(automaton see-red
(see-red : (green -> see-green)
(red -> see-red))
(see-green : (yellow -> see-yellow)
(green -> see-green))
(see-yellow : (red -> see-red))))

> (traffic-check (list 'red 'red 'red 'green))
#t
> (traffic-check (list 'red 'green 'red 'green))
#f
```

In this example, `see-red` is the initial state (follows "automaton"), and the rest of the code specifies states and their outgoing transitions. For example, in state `see-green` if the current input symbol is `yellow`, then the next state is `see-yellow`,

In a few sentences, contrast your two solutions. What are the advantages and disadvantages of each? Can you characterize the difference between the two implementations in technical languages terms?

Back to the Assignments page