_Macro
Help Contents
Workspace Management
Macros
_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:
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
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:
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 second evaluation of the macro result will do nothing. But if a STOP or OUTPUT happens, then REPEAT_DONE will output a STOP or OUTPUT instruction that will be re-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:
It's used this way:
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:
On the other hand, ` is pretty slow, since it's tree recursive and written in Logo.
See also:
Example:
References: