BERKELEY LOGO 6.0: 9. Macros

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 9. Macros

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### .macro

    .MACRO procname :input1 :input2 ...			(special form)
    .DEFMACRO procname text

A macro is a special kind of procedure whose output is evaluated as Logo instructions in the context of the macro's caller. `.MACRO` is exactly like `TO` except that the new procedure becomes a macro; `.DEFMACRO` is exactly like `DEFINE` with the same exception.

Macros are useful for inventing new control structures comparable to `REPEAT`, `IF`, and so on. Such control structures can almost, but not quite, be duplicated by ordinary Logo procedures. For example, here is an ordinary procedure version of `REPEAT`:

    to my.repeat :num :instructions
    if :num=0 [stop]
    run :instructions
    my.repeat :num-1 :instructions
    end

This version works fine for most purposes, e.g.,

    my.repeat 5 [print "hello]

But it doesn't work if the instructions to be carried out include `OUTPUT`, `STOP`, or `LOCAL`. For example, consider this procedure:

    to example
    print [Guess my secret word.  You get three guesses.]
    repeat 3 [type "|?? | ~
              if readword = "secret [pr "Right! stop]]
    print [Sorry, the word was "secret"!]
    end

This procedure works as written, but if `MY.REPEAT` is used instead of `REPEAT`, it won't work because the `STOP` will stop `MY.REPEAT` instead of stopping `EXAMPLE` as desired.

The solution is to make `MY.REPEAT` a macro. Instead of actually carrying out the computation, a macro must return a list containing Logo instructions. The contents of that list are evaluated as if they appeared in place of the call to the macro. Here's a macro version of `REPEAT`:

    .macro my.repeat :num :instructions
    if :num=0 [output []]
    output sentence :instructions ~
                    (list "my.repeat :num-1 :instructions)
    end

Every macro is an operation — it must always output something. Even in the base case, `MY.REPEAT` outputs an empty instruction list. To show how `MY.REPEAT` works, let's take the example

    my.repeat 5 [print "hello]

For this example, `MY.REPEAT` will output the instruction list

    [print "hello my.repeat 4 [print "hello]]

Logo then executes these instructions in place of the original invocation of `MY.REPEAT`; this prints `hello` once and invokes another repetition.

The technique just shown, although fairly easy to understand, has the defect of slowness because each repetition has to construct an instruction list for evaluation. Another approach is to make `MY.REPEAT` a macro that works just like the non-macro version unless the instructions to be repeated include `OUTPUT` or `STOP`:

    .macro my.repeat :num :instructions
    catch "repeat.catchtag ~
          [op repeat.done runresult [repeat1 :num :instructions]]
    op []
    end

    to repeat1 :num :instructions
    if :num=0 [throw "repeat.catchtag]
    run :instructions
    .maybeoutput repeat1 :num-1 :instructions
    end

    to repeat.done :repeat.result
    if emptyp :repeat.result [op [stop]]
    op list "output quoted first :repeat.result
    end

If the instructions do not include `STOP` or `OUTPUT`, then `REPEAT1` will reach its base case and invoke `THROW`. As a result, `MY.REPEAT`'s last instruction line will output an empty list, so the evaluation of the macro result by the caller will do nothing. But if a `STOP` or `OUTPUT` happens, then `REPEAT.DONE` will output a `STOP` or `OUTPUT` instruction that will be executed in the caller's context.

The macro-defining commands have names starting with a dot because macros are an advanced feature of Logo; it's easy to get in trouble by defining a macro that doesn't terminate, or by failing to construct the instruction list properly.

Lisp users should note that Logo macros are not special forms. That is, the inputs to the macro are evaluated normally, as they would be for any other Logo procedure. It's only the output from the macro that's handled unusually.

Here's another example:

    .macro localmake :name :value
    output (list "local          ~
                 word "" :name   ~
                 "apply          ~
                 ""make          ~
                 (list :name :value))
    end

It's used this way:

    to try
    localmake "garply "hello
    print :garply
    end

`LOCALMAKE` outputs the list

    [local "garply apply "make [garply hello]]

The reason for the use of `APPLY` is to avoid having to decide whether or not the second input to `MAKE` requires a quotation mark before it. (In this case it would — `MAKE "GARPLY "HELLO` — but the quotation mark would be wrong if the value were a list.)

It's often convenient to use the ``` function to construct the instruction list:

    .macro localmake :name :value
    op `[local ,[word "" :name] apply "make [,[:name] ,[:value]]]
    end

On the other hand, ``` is pretty slow, since it's tree recursive and written in Logo.

See section to , define , apply , stop , output .

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### .defmacro

See section .macro .

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### macrop

    MACROP name
    MACRO? name

outputs `TRUE` if its input is the name of a macro.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### macroexpand

    MACROEXPAND expr				(library procedure)

takes as its input a Logo expression that invokes a macro (that is, one that begins with the name of a macro) and outputs the the Logo expression into which the macro would translate the input expression.

    .macro localmake :name :value
    op `[local ,[word "" :name] apply "make [,[:name] ,[:value]]]
    end

    ? show macroexpand [localmake "pi 3.14159]
    [local "pi apply "make [pi 3.14159]]

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by Brian Harvey on September, 3 2008 using texi2html 1.78.