21.3 Evaluation in macros

Dylan's template macros do no evaluation. In particular, the pattern variables of a macro are unlike function parameters. They name fragments of code, rather than naming the result of the evaluation of a fragment of code.

If we were trying to write an operation like C's || (one that would evaluate expressions and would return the value of the first nonzero expression without evaluating any subsequent expressions), we could not write it as a function:

define method or-int (arg1, arg2) if (arg1 ~= 0) arg1 else arg2 end end;

When a function is invoked, all its arguments are evaluated first, which defeats our purpose. If we model our macro on our function idea, however, we will not get the ideal result either:

define macro or-int
  { or-int (?arg1:expression, ?arg2:expression) } => 
    { if (?arg1 ~= 0) ?arg1 else ?arg2 end }
end macro or-int;

The expansion of or-int (x := x + 1, y := y - 1) is probably not what we want:

if (x := x + 1 ~= 0) x := x + 1 else y := y - 1 end

We see a common macro error — the expression x := x + 1 will be evaluated twice when the resulting substitution is evaluated, leaving x with an incorrect (or at least unexpected) value. There is no magic technique for avoiding this error — you just have to be careful about repeating a pattern variable in a template. Most often, if you are repeating a pattern variable, you should be using a local variable instead, so that the fragment that the pattern represents is evaluated only once:

define macro or-int
  { or-int (?arg1:expression, ?arg2:expression) }
 => {
      let arg1 = ?arg1;
      if(arg1 ~= 0) arg1 else ?arg2 end
    }
end macro or-int;

Another potential pitfall arises if the pattern variables appear in an order in the template different from the one in which they appear in the pattern. In this case, unexpected results can occur if a side effect in one fragment affects the meaning of other fragments. In this case, you would again want to use local variables to ensure that the fragments were evaluated in their natural order.

These rules are not hard and fast: The power of macros is due in a large part to the ability of macros to manipulate code fragments without evaluating those fragments, but that power must be used judiciously. If you are designing macros for use by other people, those people may expect functionlike behavior, and may be surprised if there are multiple or out-of-order evaluations of macro parameters.

Comparison with C: Because it is more difficult to introduce local variables in C macros than it is in Dylan macros, most C programmers simply adopt the discipline of never using an expression with side effects as an argument to a macro. The problem of multiple or out-of-order evaluations of macro parameters is inherent in all macro systems, although some macro systems make it easier to handle.