There are in fact two different ways that keywords get used in macros. The first we might describe as true syntactic sugar: in compound-unit/sig, there are always exactly three clauses, and their tags, import, link, and export, serve only to remind the reader of their purpose. Similarly, each (tag : signature) clause in the import clause only contains the : for readability. In this case, rebinding the keyword identifiers will just cause invocations of the macro to fail with a syntax error, because the use of the keyword at the invocation site doesn't have the same binding as the use of the keyword in the macro definition.
The second way that keywords get used, though, is as tags to distinguish different expression forms. In this case, if the keyword gets rebound, then a subexpression of the macro invocation will actually appear as a different kind of expression, which may very well succeed at matching some other case in the macro. The cond macro serves as an example:
This macro essentially declares the "question" subexpression of each clause to have a kind of union type: a question is either an expression or the keyword else. If, at the macro invocation site, the identifier else isn't bound the same way as it was in the macro definition, it is still an expression, so it matches the second case of the macro:(define-syntax cond
[(_ [else a-exp]) a-exp]
[(_ [q0-exp a0-exp] [q1-exp a1-exp] ...)
(if q0-exp a0-exp (cond [q1-exp a1-exp] ...))]))
It's in this second case that reserving keywords is really important. In the first case, you get an error, but you find out right away and can fix it. In the second, you may not get an error at all, or the error may not appear until much later in the program.(let ([else #f])
; => #<void>