[racket] define-values/invoke-unit not working when generated by a macro

From: Carl Eastlund (cce at ccs.neu.edu)
Date: Tue Aug 6 18:05:16 EDT 2013

The concept is pretty opaque; syntax object contexts are a fairly tricky,
black-magic part of the language.  There is a rhyme and reason to them, but
it isn't terribly high level or user-friendly.  Sadly, no one yet knows how
to do better for macros as powerful as Racket's.  I've spent a long time
with them, and I got the deepest understanding from reading Kent Dybvig's
paper "Syntactic Abstraction in Scheme" over and over.  And eventually
implementing it myself, which was incidental but certainly made things
crystal clear.  I don't know that it's necessary to go that far to work
with advanced macros; mostly, writing things with default hygiene as much
as possible, and occasionally using syntax-local-introduce for tricky stuff
like macros that produce units or require forms, will get you pretty far.

Carl Eastlund


On Tue, Aug 6, 2013 at 5:57 PM, Nick Main <david.nick.main at gmail.com> wrote:

> Many thanks !
>
> The problem makes sense and the solution works.
>
> Are there any explanations of "syntax marks" beyond those in the "Syntax
> Model" section of the documentation ?  The concept seems opaque.
>
>
> On Tue, Aug 6, 2013 at 2:32 PM, Carl Eastlund <cce at ccs.neu.edu> wrote:
>
>> Nick,
>>
>> The unit system has some non-hygienic behavior, by design.  That is, it
>> introduces names that aren't part of its input, which isn't the default
>> behavior of hygienic macros.  Of course we want this -- we want the macro
>> use-dog to bind the names woof and bark, which aren't directly part of its
>> input.  But getting the context right, and still "playing nice" with other
>> hygienic macros, is slightly tricky.
>>
>> So in this context, what's the trick?  The names bound by
>> define-values/invoke-unit are bound in the context that the interface names
>> are written in.  Note that the interface names in use-dog are written
>> inside the macro definition.  The macro system assumes anything from inside
>> a macro is a local or "temporary" name that should be hidden from the rest
>> of the program.  You can fix this with the syntax-local-introduce function,
>> which (basically) toggles the context of a given syntax object between the
>> current macro expansion step and its call site.
>>
>> So right now you are getting two definitions each for woof and bark, one
>> visible by the main module and one only visible from the expansion of the
>> use-dog macro.  If you call use-dog multiple times, you'll get more,
>> separate, unique contexts.
>>
>> You can fix it like this, to make use-dog bind things in the context
>> where it is called:
>>
>>     (define-syntax (use-dog stx)
>>       (syntax-case stx ()
>>         ([_ dog-unit]
>>          #`(define-values/invoke-unit dog-unit
>>              (import)
>>              (export #,(syntax-local-introduce #'dog^))))))
>>
>> Carl Eastlund
>>
>> On Tue, Aug 6, 2013 at 5:11 PM, Nick Main <david.nick.main at gmail.com>wrote:
>>
>>> I am attempting to write a macro to clean up the use
>>> of define-values/invoke-unit and finding some confusing behavior.
>>>
>>>  My macros module is:
>>>
>>> #lang racket
>>> (provide (all-defined-out))
>>>
>>> (define-signature dog^
>>>   (woof
>>>    bark))
>>>
>>> (define mutt@
>>>   (unit
>>>     (import)
>>>     (export dog^)
>>>     (define (woof) (printf "Wuf !!\n"))
>>>     (define (bark) (printf "RarRarRar !!\n"))))
>>>
>>> (define-syntax use-dog
>>>   (syntax-rules ()
>>>     ([_ dog-unit]
>>>      (define-values/invoke-unit dog-unit
>>>        (import)
>>>        (export dog^)))))
>>>
>>>
>>> ..and the module using it is:
>>>
>>> #lang racket
>>> (require "macros.rkt")
>>>
>>> (define-values/invoke-unit mutt@
>>>   (import)
>>>   (export dog^))
>>>
>>>  (use-dog mutt@)
>>>
>>> (woof)
>>>  (woof)
>>> (bark)
>>> (woof)
>>>
>>>
>>> I am trying to make the "use-dog" macro expand to the equivalent
>>> define-values/invoke-unit form as shown.
>>> If I comment out the define-value/invoke-unit form I get warning about
>>> unbound identifier woof - implying that the (use-dog dog^) form is not
>>> doing the job. Moreover, the second module as it stands does not give a
>>> warning about duplicate definitions for woof or bark (as it does if I
>>> duplicate the define-values/invoke-unit form) - further indicating the
>>> non-action of use-dog.
>>>
>>> The macro stepper shows use-dog expand exactly as expected, but it then
>>> seems to be ignored without any warnings.
>>>
>>> Is there something I am misunderstanding here, or is this a bug ?
>>>
>>> ____________________
>>>   Racket Users list:
>>>   http://lists.racket-lang.org/users
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20130806/bb962a91/attachment-0001.html>

Posted on the users mailing list.