Macros ====== .. index:: ! macro The term *macro*, as used in computer programming, originally stood for *macro-instruction*, meaning an instruction that represented a sequence of several machine (or micro) instructions. Over time, the term has evolved to mean any word or phrase that stands for another phrase (usually longer, but built of simpler components). Macros can be used for abbreviation, abstraction, simplification, or structuring. Many application programs, such as word processors or spreadsheets, offer a macro language for writing scripts or subroutines that bundle a number of simpler actions into one command. Many computer languages support a macro facility for creating shorthand notations for commonly used, longer phrases. They range from simple, text-based abbreviations to full languages, permitting computed replacements. Macros are processed before the program is compiled by *expanding* each macro into its replacement phrase as that macro is encountered until there are no more macros. You can use macros to extend the base language by defining more sophisticated phrases in terms of simpler, built-in phrases. The primary use of macros in programming languages is to extend or adapt the language to allow a more concise or readable solution for a particular problem domain. A simple program rarely needs macros. More complicated programs, including the implementation of a Dylan compiler and run-time system, will use macros often. Macros have no visible run-time cost or effect — they are transformations that take place during the compilation of a program (hence, they *can* increase compilation time). Although macros may take the form of function calls, they are not functions — they cannot be passed as functional arguments, and they cannot be invoked in a run-time image as a function can. Although macros may have parameters, they do not take arguments the way functions do. The arguments to a macro are not evaluated; they are simply program phrases that can be substituted in the replacement phrase. Dylan provides a macro facility that is based on pattern matching and template substitution. This facility is more powerful than is a simple textual substitution facility, but is simpler than a *procedural-macro* facility, which allows arbitrary computations to construct replacement phrases. Dylan’s macro facility is closely integrated with the Dylan language syntax, and permits most macro needs to be satisfied. Dylan designers have also planned for a full procedural macro capability, so that it can be added compatibly at a later time if there is sufficient demand. .. topic:: Comparison with C and C++: C and C++ macros are text substitutions, performed by a preprocessor. The preprocessor has no understanding of the language; it simply splices together text fragments to create replacement phrases. Dylan macros are written in terms of Dylan language elements; the macros choose their transformation by pattern matching, and they substitute program fragments. Language-based macros are more powerful than — and avoid a number of common pitfalls of — text-substitution macros. These pitfalls are described in later comparisons in this chapter. .. index:: single: patterns single: macro; patterns single: templates single: macro; templates single: macro; rules Patterns and templates ---------------------- A Dylan macro consists of a set of *rules*. Each rule has two basic parts: a *pattern* that is matched against a fragment of code, and a *template* that is substituted for the matched fragment, perhaps including pieces of the original fragment. When a macro is invoked, each rule is tried in order until a matching pattern is found. When a match is found, the macro is replaced by the matching template. If no match can be found, an error occurs. Dylan macros are recognized by the compiler because they fit one of three possible formats: the function macro, the statement macro, and the defining macro. The macro format determines the overall fragment that is matched against the macro’s rules at each macro invocation. .. index:: single: macro; function macro single: function macro The simplest macro format that the compiler can match is that of a function call. A *function macro* is invoked in exactly the same way that a function is invoked. The name of the macro is a module variable that can be used anywhere a function call can occur. Typically, it is simply the name followed by a parenthesized list of arguments, but recall that slot-style abbreviations and unary and binary operators are also function calls. .. index:: single: macro; delay of evaluation of arguments The most important use of function macros is to rearrange or delay evaluation of arguments. The fragment that is matched against the function macro’s rules is the phrase that represents a function’s arguments. The function macro can then rearrange the function arguments, perhaps adding code. When a macro rearranges its arguments, its action has the effect of delaying the evaluation of the arguments (as opposed to a function call, where the argument expressions are evaluated and then passed to the function). One simple use of delaying evaluation is to write a function-like construct similar in spirit to C’s ``?:`` operator: .. code-block:: dylan define macro if-else { if-else (?test:expression, ?true:expression, ?false:expression) } => { if (?test) ?true else ?false end } end macro if-else; We could not write ``if-else`` as a function, because both the true and false expressions would be evaluated before the function was even called: .. code-block:: dylan-console ? define variable *x* = 0; ? define variable *y* = 0; ? *y* := if-else(*y* == 0, *x* := 1, *x* := -1); => 1 ? *y*; => 1 ? *x*; => 1 If we had defined ``if-else`` as a function, ``*x*`` would have been ``-1``, rather than ``1``, because both assignments to ``*x*`` would have been evaluated, before ``if-else`` was called. When a macro is used, the assignments are just substituted into the template ``if``, which evaluates the first clause only when the condition is true. .. index:: single: macro; pattern variables single: pattern variables Looking at the macro definition of ``if-else``, we can infer basic ideas about macros. A macro is introduced by ``define macro``, followed by the *macro name* — in this case, ``if-else``. The definition of the macro is a *rule* that has two parts: a *pattern* enclosed in braces, *{}*, that mimics the fragment that it is to match, and a *replacement*. Macro parameters, called *pattern variables*, are introduced in the pattern by ``?``. They match fragments with particular *constraints* — in this case, ``:expression``. They are delimited by punctuation — in this case, the open and close parentheses, ``()``, and the comma, ``,``. The replacement part of the rule, the *expansion*, is indicated by ``=>`` and is defined by a *template*, also enclosed in braces. The template is in the form of a code fragment, where pattern variables are used to substitute in the fragments they matched in the pattern. Note that matching and replacement are language based, so required and optional whitespace is treated exactly as in Dylan. We have used optional whitespace to improve the legibility of the macro definitions presented here. Most Dylan development environments provide a way to view code after all macros have been expanded. This view can be helpful in debugging macros that you write. For example, showing the expanded view of an expression like .. code-block:: dylan *y* := if-else(*y* == 0, *x* := 1, *x* := -1); might yield .. code-block:: dylan *y* := if (*y* == 0) *x* := 1 else *x* := -1 end; The exact format of the expanded view of the macro depends on the particular development environment. Here, we show the code that comes from the macro template in *underlined italic*, whereas the fragments matched by the pattern variables and substituted into the template are presented in our conventional *code font*. Note that the ``if-else`` macro we have defined is just syntactic sugar — Dylan’s built-in ``if`` statement is perfectly sufficient for the job. .. index:: single: macro; delay of evaluation of arguments Another reason to delay evaluation is to change the value of an argument — for example, to implement an operator similar in spirit to C’s ``++`` and ``+=`` operators: .. code-block:: dylan define macro inc! { inc! (?place:expression, ?by:expression) } => { ?place := ?place + ?by; } { inc! (?place:expression) } => { ?place := ?place + 1; } end macro inc!; This macro might be used as follows: .. code-block:: dylan-console ? define variable *x* = 0; ? inc!(*x*, 3); => 3 ? *x*; => 3 ? inc!(*x*); 4 ? *x*; 4 In this macro, it is important to delay the evaluation of the first argument because we want to be able to assign to the variable or slot it is stored in, rather than simply to manipulate the value of the variable or slot. The ``inc!`` macro demonstrates the use of multiple rules in a macro. They are tried in order until an appropriate match is found. This allows the ``inc!`` macro to have two forms. The one-argument form increments the argument by 1. The two-argument form allows the increment amount to be specified. .. index:: single: macro; hygiene Macro hygiene ------------- Displaying the code fragments inserted by the macro in *underlined italics* both helps to show exactly what the macro has done to our code, and draws attention to an important feature of Dylan macros — they are hygienic macros. A *hygienic* or *referentially transparent* macro system is one that prevents accidental collisions of macro variables with program variables of the same name. Consider the following macro, which is used to exchange the values of two variables: .. code-block:: dylan define macro swap! { swap! (?place1:expression, ?place2:expression) } => { let value = ?place1; ?place1 := ?place2; ?place2 := value } end macro swap!; The local variable ``value`` is created by the macro. There is a possibility that this variable could conflict with another variable in the surrounding code. Consider what might happen if we were to expand ``swap!(value, x)``: .. code-block:: dylan let value = value; value := x; x := value With simple textual substitutions, ``swap!`` would have no effect in this case. Dylan’s hygienic macros solve this problem by differentiating between the ``value`` introduced by the macro and any other ``value`` that might appear in the original code. .. topic:: Comparison with C: Because C (and C++) macros are simply text substitutions performed by a preprocessor that has no understanding of the C language, they are inherently unhygienic. C macro writers reduce this problem by choosing unusual or unlikely names for local variables in their macros (such as ``_swap_temp_value``), but even this workaround can be insufficient in complex macros. Dylan macros in effect automatically rename macro variables on each expansion to guarantee unique names. .. index:: single: macro; evaluation in 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: .. code-block:: dylan 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: .. code-block:: dylan 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: .. code-block:: dylan 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: .. code-block:: dylan 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 function-like behavior, and may be surprised if there are multiple or out-of-order evaluations of macro parameters. .. topic:: 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. .. index:: single: macro; constraints Constraints ----------- .. index:: single: macro; statement macro single: statement macro So far, in our macros, we have seen the constraint *expression* used for the pattern variables. Except for a few unusual cases, pattern variables must always have a constraint associated with them. Constraints serve two purposes: they limit the fragment that the pattern variable will match, and they define the meaning of the pattern variable when it is substituted. As an example, consider the following *statement macro*, which we might find useful for manipulating the decoded parts of seconds: .. code-block:: dylan define macro with-decoded-seconds { with-decoded-seconds (?max:variable, ?min:variable, ?sec:variable = ?time:expression) ?:body end } => { let (?max, ?min, ?sec) = decode-total-seconds(?time); ?body } end macro; The preceding macro might be used as follows: .. code-block:: dylan define method say (time ::