Hi all!

--------------------

TL;DR: if you're struggling when debugging scopes, I wrote a couple of 
utilities to help: (+scopes stx) shows the scopes in a readable manner, 
(print-full-scopes) gives a summary of these scopes, and (make-named-scope 
string-or-symbol) creates a fresh scope annotated with a name, to track it more 
easily in the macro stepper or in (print-full-scopes). Docs here:

http://docs.racket-lang.org/debug-scopes/

--------------------

When developing unhygienic macros, it frequently occurs that an extra, unwanted 
scope is present on one part of the output: In the macro's result, one might 
expect two identifiers to bind one another, but they won't because of that 
extra scope.

The reasons for this occurring are rare, but varied:
* Using a macro which forgets to remove the use-site scope (with 
syntax-local-identifier-as-binding) from an identifier which ends up in a 
binding position. Most of the time this is not a problem, but in a few corner 
cases this can be a problem.
* Template metafunctions (from syntax/parse/experimental/template) flip their 
own mark on the output, but don't provide a way to cancel that mark (i.e. they 
are forcefully hygienic in a way).
* Voluntarily applying a (make-syntax-introducer) in one place, and mistakenly 
forgetting to apply it in the other
* And so on.

While the fix for these problem is often trivial (flip the right scope in the 
right place), finding the source of the error can be a difficult endeavour.

A few things could be changed to help debugging:
* The snip% that shows up when a syntax object is display-ed shows a wealth of 
information about the syntax object and its subparts, but not the scopes 
present on them.
* The macro-debugger/syntax-browser does show this information, but can only be 
called from phase 0 (rendering difficult attempts to inspect syntax objects in 
the middle of the execution of a transformer function)
* The macro stepper is a bit cumbersome to use for this purpose: you have to 
click on the two identifiers one after another, and compare the (potentially 
long) list of scopes. Also, it rarely crashes, but according to Murphy's law 
will only do so when you're debugging the hardest issue ;-)
* The scopes as printed by syntax-debug-info and the macro debugger are 6-digit 
numbers which are difficult to compare at a glance
* These numbers say nothing about which macro introduced which scope — names 
would be a bliss, but alas (make-syntax-introducer) does not accept an optional 
name argument, and the auto-generated macro and use-site scopes are not 
annotated with the macro's name.

I wrote a small package, https://pkgd.racket-lang.org/pkgn/package/debug-scopes 
, which helps with the last two issues:

* The (+scopes stx) function returns a string, where each identifier is adorned 
with the scopes it has, using a succinct notation for ranges of scopes, which 
is practical for comparing the sets of scopes on two identifiers at a glance 
(see the docs for more details).
* The (print-full-scopes) function prints a table making the correspondance 
between that succinct notation and the traditional syntax-debug-info notation
* The (make-named-scope string-or-symbol) function creates a fresh scope like 
(make-syntax-introducer), but annotates it with a name. It is based on a hack: 
it creates a module with that name on the fly, extracts the module scope, and 
discards the module. This works because module scopes are annotated with their 
name in the macro debugger, unlike other kinds of scopes. This hack is likely 
slow, but if someday (make-syntax-introducer) supports an optional name, we'll 
rely on that instead.
* As an experimental feature, (require debug-scopes/named-scopes/override) 
overrides define-syntax and syntax-local-introduce, so that the implicit macro 
scope bears the name of the macro (it does not affect the use-site scope, as 
that one is handled specially by definition contexts). This feature might 
behave badly with other for-syntax functions which use the non-overridden 
version of syntax-local-introduce, so use with care.

I hope this can help others while debugging scope-related issues, which I found 
to be the most difficult aspect of unhygienic macro development.

Happy new year!
Georges Dupéron

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to