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

From: Carl Eastlund (cce at ccs.neu.edu)
Date: Tue Feb 1 15:20:52 EST 2011

On Tue, Feb 1, 2011 at 3:09 PM, Matthew Flatt <mflatt at cs.utah.edu> wrote:
> 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.

Thanks for the response, Matthew.  I wound up concluding I was better
off using different names anyway, so I think the status quo is okay.
However, the error message could be more informative.  For instance:

"compile: reference to <name> in phase <i> is out of the scope of
<name> defined in phase <j> at <srcloc>; bindings in all phases must
be in scope."

--Carl



Posted on the dev mailing list.