[racket-dev] Phase distinctions and "compile: identifier used out of context"

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Tue Feb 1 15:09:52 EST 2011

At Sat, 29 Jan 2011 15:07:37 -0500, Carl Eastlund wrote:
> The following program binds => at both phase 0 (module-scoped macro)
> and phase 1 (lexically-scoped value).  The use of the => macro at the
> end expands into a recursive reference to =>, which in turn produces
> the error "compile: identifier used out of context".  As I understand
> it, this happens because the identifier => has been taken out of the
> scope of its phase 1 binding.  However, it is being used here at phase
> 0.  I would much prefer if the expander simply ignored the phase 1
> binding and carried out the recursive macro invocation.
> 
> Before I file this as a bug report, have I understood what's going on?
>  Is there any reason this shouldn't or couldn't be fixed?

It would indeed be sensible to have local bindings at phase N shadow
only at phase N, leaving bindings in other phases as they are.

We haven't done that so far for two reasons:

 * They're local bindings, so you can always pick different identifiers
   for bindings that have overlapping scopes.

 * If you use the same identifier for bindings with overlapping scopes
   at different phases, the results can be confusing.

As an example of the latter, consider

     (syntax-case stx ()
       [(_ id)
        (let ([id (munge #'id)])
          #'(list id))])

versus

     (syntax-case stx ()
       [(_ id)
        (with-syntax ([id (munge #'id)])
          #'(list id))])

Currently, only the latter is sensible, and the former leads to a phase
error. If the two `id's were bound only in their respective phases
(i.e., 0 and -1), then the former example's result template would use
`id' from the pattern, quietly ignoring the `let' binding.

I don't think that's a great example, but it has the flavor of a kind
of mistake that I make. Given the options, I think I prefer to rename
local variables that collide to trigger errors, instead of having
bindings that look like they shadow lead to something different
(usually quietly). But I haven't actually tried programming with the
other option.

Of course, with respect to the possibility of confusion, it's just as
potentially confusing to use the same identifier for imported bindings
different phases. For example, it can be confusing that `lambda' is
available at multiple phases, perhaps even with different meanings. In
contrast to just local bindings, however, picking different names for
all bindings in different phases --- including alternatives for
`lambda' at different phases --- would be truly inconvenient.



Posted on the dev mailing list.