[racket-dev] When is it safe to not rename a runtime value in a macro?
On 08/25/2012 12:19 PM, Neil Toronto wrote:
> A lot of macros start by renaming some syntax that's assumed to
> eventually represent a runtime value, like the `or' macro does:
>
> > (syntax->datum (expand-syntax #'(or #t #f)))
> '(let-values (((or-part) '#t)) (if or-part or-part '#f))
>
> But it's not always a good thing, particularly when the output will be
> processed by another macro, and parts will be tested using an
> `identifier=?' function. For example, my `inline-sort' takes `<' as an
> argument. So
>
> (inline-sort < a b)
>
> expands to
>
> (let ([lt? <] [temp1 a] [temp2 b])
> (if (lt? temp1 temp2) (values temp1 temp2) (values temp2 temp1)))
>
> Well, it did at first. When I discovered that Typed Racket's optimizer
> would never detect that `lt?' is actually `<' and replace it with
> `unsafe-fl<', I altered the macro to detect when the `<' is an
> identifier, and not rename it with a `let'.
Is it feasible to make the TR optimizer smarter about this? That would
be the best solution.
> I know that I should assert my rights under the Macro Bill. But I like
> to program defensively. So what's safe to not rename? I think the
> following are:
I've reordered these a bit:
> number
> string
> bytes
> character
> regexp
In other words, "literal data". But did you check that the '#%datum'
macro associated with them has the standard meaning? If not, they could
expand into arbitrary expressions (possibly with side effects)!
> symbol
Do you mean identifier, as in "it's just a variable reference"? But it
could be an identifier macro. Or it could be a variable that other parts
of the code could concurrently mutate. For example, suppose 'match'
expanded less cautiously than it actually does:
(match x [(cons a b) a])
=>
(if (pair? x) (unsafe-car x) (match-error ....))
Now suppose that x is a module-level variable that some other thread
mutates between the 'pair?' check and the 'unsafe-car'.
> null
> prefab struct key
I don't know what you mean by these in this context. Maybe 'quote'
expressions containing them?
--
If you must do such things, the safest way in general is to fully
local-expand the expression you want to analyze; that gives you
something with a known grammar. Now it's feasible to identify literal
data: it's any 'quote' expression. You don't have to worry about
identifier macros, but the 'set!' danger remains for any variables that
you don't completely control the scope of.
In your specific case, it would also be safe to check whether the
comparison expression is an identifier free-identifier=? to one of a
fixed list of known (immutable) variables; if so, it's safe to duplicate.
> Also, should I ever have to intern syntax that's one of these kinds of
> things?
Do you mean intern the data? IIUC, yes: the reader interns literal data,
but macros are free to introduce non-interned literal data. (It will
probably get interned again when the compiled form is loaded from zo,
but there's a period during the expansion of the enclosing module when
it won't be interned.)
Ryan