User Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cl:macros:defmacro [2019/08/16 12:00]
cl:macros:defmacro [2019/12/13 00:00] (current)
Line 1: Line 1:
 +====== Macro DEFMACRO ======
 +
 +====Syntax====
 +  * **defmacro** //name lambda-list <​nowiki>​[[</​nowiki>​declaration''​*''​ | documentation<​nowiki>​]]</​nowiki>​ form''​*''//​ → //name//
 +
 +====Arguments and Values====
 +  * //name// - a //​[[CL:​Glossary:​symbol]]//​.
 +  * //​lambda-list//​ - a //​[[CL:​Glossary:​macro lambda list]]//.
 +  * //​declaration//​ - a **[[CL:​Symbols:​declare]]** //​[[CL:​Glossary:​expression]]//;​ not evaluated.
 +  * //​documentation//​ - a //​[[CL:​Glossary:​string]]//;​ not evaluated.
 +  * //form// - a //​[[CL:​Glossary:​form]]//​.
 +
 +====Description====
 +Defines //name// as a //​[[CL:​Glossary:​macro]]//​ by associating a //​[[CL:​Glossary:​macro function]]//​ with that //name// in the global environment.
 +
 +The //​[[CL:​Glossary:​macro function]]//​ is defined in the same //​[[CL:​Glossary:​lexical environment]]//​ in which the **[[CL:​Macros:​defmacro]]** //​[[CL:​Glossary:​form]]//​ appears.
 +
 +The parameter variables in //​lambda-list//​ are bound to destructured portions of the macro call.
 +
 +The expansion function accepts two arguments, a //​[[CL:​Glossary:​form]]//​ and an //​[[CL:​Glossary:​environment]]//​. The expansion function returns a //​[[CL:​Glossary:​form]]//​. The body of the expansion function is specified by //forms//. //Forms// are executed in order. The value of the last //form// executed is returned as the expansion of the //​[[CL:​Glossary:​macro]]//​.
 +
 +The body //forms// of the expansion function (but not the //​lambda-list//​) are implicitly enclosed in a //​[[CL:​Glossary:​block]]//​ whose name is //name//.
 +
 +The //​lambda-list//​ conforms to the requirements described in {\secref\MacroLambdaLists}.
 +
 +//​Documentation//​ is attached as a //​[[CL:​Glossary:​documentation string]]// to //name// (as kind **[[CL:​Special Operators:​function]]**) and to the //​[[CL:​Glossary:​macro function]]//​.
 +
 +**[[CL:​Macros:​defmacro]]** can be used to redefine a //​[[CL:​Glossary:​macro]]//​ or to replace a //​[[CL:​Glossary:​function]]//​ definition with a //​[[CL:​Glossary:​macro]]//​ definition.
 +
 +Recursive expansion of the //​[[CL:​Glossary:​form]]//​ returned must terminate, including the expansion of other //​[[CL:​Glossary:​macro|macros]]//​ which are //​[[CL:​Glossary:​subform|subforms]]//​ of other //​[[CL:​Glossary:​form|forms]]//​ returned.
 +
 +The consequences are undefined if the result of fully macroexpanding a //​[[CL:​Glossary:​form]]//​ contains any //​[[CL:​Glossary:​circular]]//​ //​[[CL:​Glossary:​list structure]]//​ except in //​[[CL:​Glossary:​literal object|literal objects]]//​.
 +
 +If a **[[CL:​Macros:​defmacro]]** //​[[CL:​Glossary:​form]]//​ appears as a //​[[CL:​Glossary:​top level form]]//, the //​[[CL:​Glossary:​compiler]]//​ must store the //​[[CL:​Glossary:​macro]]//​ definition at compile time, so that occurrences of the macro later on in the file can be expanded correctly. Users must ensure that the body of the //​[[CL:​Glossary:​macro]]//​ can be evaluated at compile time if it is referenced within the //​[[CL:​Glossary:​file]]//​ being //​[[CL:​Glossary:​compiled]]//​.
 +
 +====Examples====
 +<​blockquote>​
 +(defmacro mac1 (a b)
 +  "Mac1 multiplies and adds"
 +  `([[CL:​Functions:​math-add|+]] ,a ([[CL:​Functions:​math-multiply|*]] ,b 3)))
 +<​r>​MAC1 </r>
 +(mac1 4 5) <r>19 </r>
 +([[CL:​Functions:​documentation]] 'mac1 '​[[CL:​Types:​function]]) <​r>"​Mac1 multiplies and adds"</​r>​
 +
 +(defmacro mac2 (&​optional (a 2 b) (c 3 d) &rest x)
 +  `'(,a ,b ,c ,d ,x))
 +<​r>​MAC2</​r>​
 +(mac2 6) <r>(6 [[CL:​Constant Variables:​t|T]] 3 [[CL:​Constant Variables:​nil|NIL]] [[CL:​Constant Variables:​nil|NIL]])</​r>​
 +(mac2 6 3 8) <r>(6 [[CL:​Constant Variables:​t|T]] 3 [[CL:​Constant Variables:​t|T]] (8))</​r>​
 +
 +(defmacro mac3 (&whole r a &​optional (b 3) &rest x &key c (d a))
 +  `'(,r ,a ,b ,c ,d ,x))
 +<​r>​MAC3</​r>​
 +(mac3 1 6 :d 8 :c 9 :d 10) 
 +<​r>​((MAC3 1 6 :D 8 :C 9 :D 10) 1 6 9 8 (:D 8 :C 9 :D 10))</​r>​
 +</​blockquote>​
 +
 +The stipulation that an embedded //​[[CL:​Glossary:​destructuring lambda list]]// is permitted only where //​[[CL:​Glossary:​ordinary lambda list]]// syntax would permit a parameter name but not a //​[[CL:​Glossary:​list]]//​ is made to prevent ambiguity. For example, the following is not valid:
 +
 +<​blockquote>​
 +(defmacro loser (x &​optional (a b &rest c) &rest z) 
 +  ...)
 +</​blockquote>​
 +
 +because //​[[CL:​Glossary:​ordinary lambda list]]// syntax does permit a //​[[CL:​Glossary:​list]]//​ following ''&​optional'';​ the list ''​(a b &rest c)''​ would be interpreted as describing an optional parameter named ''​a''​ whose default value is that of the form ''​b'',​ with a supplied-p parameter named ''&​rest''​ (not valid), and an extraneous symbol ''​c''​ in the list (also not valid). An almost correct way to express this is
 +
 +<​blockquote>​
 +(defmacro loser (x &​optional ((a b &rest c)) &rest z) 
 +  ...)
 +</​blockquote>​
 +
 +The extra set of parentheses removes the ambiguity. However, the definition is now incorrect because a macro call such as ''​(loser (car pool))''​ would not provide any argument form for the lambda list ''​(a b &rest c)'',​ and so the default value against which to match the //​[[CL:​Glossary:​lambda list]]// would be **[[CL:​Constant Variables:​nil]]** because no explicit default value was specified. The consequences of this are unspecified since the empty list, **[[CL:​Constant Variables:​nil]]**,​ does not have //​[[CL:​Glossary:​form|forms]]//​ to satisfy the parameters ''​a''​ and ''​b''​. The fully correct definition would be either
 +
 +<​blockquote>​
 +(defmacro loser (x &​optional ((a b &rest c) '(nil nil)) &rest z)
 +  ...)
 +</​blockquote> ​
 +
 +or
 +
 +<​blockquote>​
 +(defmacro loser (x &​optional ((&​optional a b &rest c)) &rest z) 
 +  ...)
 +</​blockquote>​
 +
 +These differ slightly: the first requires that if the macro call specifies ''​a''​ explicitly then it must also specify ''​b''​ explicitly, whereas the second does not have this requirement. For example,
 +
 +<​blockquote>​
 +(loser ([[CL:​Functions:​car]] pool) (([[CL:​Functions:​math-one-plus|1+]] x)))
 +</​blockquote>​
 +
 +would be a valid call for the second definition but not for the first.
 +
 +<​blockquote>​
 +(defmacro dm1a (&whole x) `',x) <​r>​DM1A</​r>​
 +([[CL:​Functions:​macroexpand]] '​(dm1a)) <​r>​([[CL:​Special Operators:​quote|QUOTE]] (DM1A))</​r>​
 +([[CL:​Functions:​macroexpand]] '(dm1a a))
 +<​e>​Error:​ while parsing arguments to DEFMACRO DM1A:
 +exactly 0 expected, but got 1.</​e>​
 +
 +(defmacro dm1b (&whole x a &​optional b) `'(,x ,a ,b)) <​r>​DM1B</​r>​
 +([[CL:​Functions:​macroexpand]] '​(dm1b))
 +<​e>​Error:​ while parsing arguments to DEFMACRO DM1A:
 +between 1 and 2 expected, but got 0.</​e>​
 +([[CL:​Functions:​macroexpand]] '(dm1b q)) <​r>​([[CL:​Special Operators:​quote|QUOTE]] ((DM1B Q) Q NIL))</​r>​
 +([[CL:​Functions:​macroexpand]] '(dm1b q r)) <​r>​([[CL:​Special Operators:​quote|QUOTE]] ((DM1B Q R) Q R))</​r>​
 +([[CL:​Functions:​macroexpand]] '(dm1b q r s))
 +<​e>​Error:​ while parsing arguments to DEFMACRO DM1A:
 +between 1 and 2 expected, but got 3.</​e>​
 +
 +(defmacro dm2a (&whole form a b)
 +  `'​(form ,form a ,a b ,b))
 +([[CL:​Functions:​macroexpand]] '(dm2a x y)) <​r>​([[CL:​Special Operators:​quote|QUOTE]] (FORM (DM2A X Y) A X B Y))
 +T</r>
 +(dm2a x y) <​r>​(FORM (DM2A X Y) A X B Y)</​r>​
 +
 +(defmacro dm2b (&whole form a (&whole b (c . d) &​optional (e 5)) 
 +                &body f &​environment env) 
 +  ``(,',​form ,,a ,',b ,',​([[CL:​Functions:​macroexpand]] c env) ,',d ,',e ,',f))
 +([[CL:​Functions:​macroexpand]] '(dm2b x1 ((([[CL:​Macros:​incf]] x2) x3 x4)) x5 x6)) 
 +<​r>​(LIST* '(DM2B X1 ((([[CL:​Macros:​incf|INCF]] X2) X3 X4)) X5 X6) X1 '​(((([[CL:​Macros:​incf|INCF]] X2) X3 X4)) ([[CL:​Macros:​setf|SETF]] X2 ([[CL:​Functions:​math-one-plus|1+]] X2)) (X3 X4) 5 (X5 X6)))
 +[[CL:​Constant Variables:​T]] </r>
 +</​blockquote>​
 +
 +In the above example, note that because backquote is involved, implementations may differ slightly in the nature (though not the functionality) of the expansion.
 +
 +<​blockquote>​
 +([[CL:​Special Operators:​let]] ((x1 5)) 
 +  ([[CL:​Special Operators:​macrolet]] ((segundo (x) `([[CL:​Functions:​cadr]] ,x)))
 +    (dm2b x1 (((segundo x2) x3 x4)) x5 x6)))
 +<​r>​((DM2B X1 (((SEGUNDO X2) X3 X4)) X5 X6) 5 
 + ​(((SEGUNDO X2) X3 X4)) (CADR X2) (X3 X4) 5 (X5 X6)) </r>
 +</​blockquote>​
 +
 +====Affected By====
 +None.
 +
 +====Exceptional Situations====
 +None.
 +
 +====See Also====
 +**[[CL:​Macros:​define-compiler-macro|Macro DEFINE-COMPILER-MACRO]]**,​ **[[CL:​Macros:​destructuring-bind|Macro DESTRUCTURING-BIND]]**,​ **[[CL:​Functions:​documentation|Generic Function DOCUMENTATION]]**,​ **[[CL:​Functions:​macroexpand|Function MACROEXPAND]]**,​ **[[CL:​Variables:​star-macroexpand-hook-star|Variable *MACROEXPAND-HOOK*]]**,​ **[[CL:​Special Operators:​macrolet|Special Operator MACROLET]]**,​ **[[CL:​Functions:​macro-function|Function MACRO-FUNCTION]]**,​ {\secref\Evaluation},​ {\secref\Compilation},​ {\secref\DocVsDecls}
 +
 +====Notes====
 +None.
 +
 +\issue{COMPILE-FILE-HANDLING-OF-TOP-LEVEL-FORMS:​CLARIFY} \issue{DEFMACRO-LAMBDA-LIST:​TIGHTEN-DESCRIPTION} \issue{DEFINE-COMPILER-MACRO:​X3J13-NOV89} \issue{DECLS-AND-DOC} \issue{DEFINING-MACROS-NON-TOP-LEVEL:​ALLOW} \issue{FLET-IMPLICIT-BLOCK:​YES} \issue{DEFMACRO-BLOCK-SCOPE:​EXCLUDES-BINDINGS} \issue{DOCUMENTATION-FUNCTION-BUGS:​FIX} \issue{RECURSIVE-DEFTYPE:​EXPLICITLY-VAGUE}