Macros Quick Review
Typically, when we call a procedure we follow three steps:
Evaluate the operator
Evaluate the operands
Apply the operator to the operands
However, with macros, we do the same steps, but in a slightly different order:
Evaluate the operator
Apply the operator to the operands
Evaluate the operands
Why macros? Well sometimes we don’t want to evaluate our operands before we run some procedure. A simple example of this is if we wanted to write a function that will repeat an operation twice:
(define (twice expr) (begin expr expr))
If we want to repeat the operation (print 2), we will be unable to do that because once our operand (print 2) is evaluated, it will evaluate to none, so the begin statement will just end up evaluating none twice, instead of evaluating the print statement twice.
Now how does the macro evaluation procedure fix this problem? Well, when we write a macro, if we leave the operands unevaluated, we can fill them into the procedure that we want and then evaluate them to ensure that they are getting evaluated when we want them to be.
The goal is to fill in our macro’s unevaluated operands into some sort of list and then return that list. Why a list? Well, we know that when we run a macro, we first apply our procedure and then evaluate our operands. If we fill in the unevaluated operands into a list, then evaluate them, and then return the list, the final solution that is returned from our function will be the result of evaluating that list. Remember, in Python, if we try to return 1 + 2, our actual return value will be 3. Similarly, in Scheme, if we return (if 0 1 2), our actual return value will be 2.
How To write a macro
Writing a macro sounds harder than it is; in fact, you can break it down into two steps, both of which you already learned how to do earlier in the scheme unit.
Write out the expression that you want the macro to evaluate when it is run
Construct the list that will create that expression
Basically, all you have to do is write out an expression in scheme, and then create a list. Nice!
LET’S SEE IT ON AN EXAMPLE
Let me actually show this macro-writing process on an example, since it still might seem a little unclear.
Let’s create a macro that represents an if statement, and takes in a condition, a true result, and a false result.
(define (if-macro cond true-res false-res)
Okay, so the first step is to write out the expression that we want the macro to evaluate when it is run. We know that we want the body of our macro to evaluate an if statement, so we probably want an if statement, which we already know how to write from the scheme unit.
(if cond true-res false-res)
Cool, got the first step down. Now let’s construct a list that will return that expression. If we look at the above expression, it looks like it’s just a list of four elements, if, cond, true-res, and false-res. Do we know how to make a list of four elements? Yes, we’ve already learned that too!
(list ‘if cond true-res false-res)
Believe it or not, the above line is actually the body of our macro. So our final answer should look like this:
(define (if-macro cond true-res false-res) (list ‘if cond true-res false-res))
We can check this by running our macro on a very simple example:
(if-macro (= 1 0) (+ 1 2) (+ 3 4))
We will first apply the macro, which will give us the following list:
(if (= 1 0) (+ 1 1) (+ 3 4))
Then we will evaluate the operands, which will give us the following list:
(if #f 2 7)
Since this return value is an expression, we have to actually evaluate it (think about how in Python, we would never return something like 1 + 2, instead we would evaluate it to 3 and then return 3). We will evaluate this expression and return 7.