[racket] Scoped require

From: Maxim Romashchenko (max at anahoret.com)
Date: Wed Aug 24 08:42:51 EDT 2011

Hello Carl.

This is it! Fits perfectly. And works as expected :-)

I see your point regarding just using local-require, but in this 
particular case I was looking for more concise code.

Thanks a lot! I appreciate your help.

Best regards, Maxim.


On 2011-08-23 15:03, Carl Eastlund wrote:
> Maxim,
>
> It is possible to write a macro that "hides" local-require, although
>
> (let ()
>    (local-require 'names)
>    my-code ...)
>
> looks about as simple to me as
>
> (require 'names)
> (my-begin
>    my-code ...)
>
> and the former does not require implementing any new unhygienic
> macros.  The local-require macro is itself unhygienic, but at least it
> has already been implemented.  Still, here's how you can write your
> macro:
>
> (require
>    (for-syntax syntax/parse)
>    racket/splicing)
> (define-syntax (my-begin stx)
>    (syntax-parse stx
>      [(_ body:expr ...)
>       (with-syntax
>           ([imports
>             (datum->syntax stx
>               (syntax-e (syntax 'real-names)))])
>         (syntax
>           (splicing-local
>             [(local-require imports)]
>             body ...)))]))
>
> Here, "real-names" is the name of the module providing everything that
> will be bound within my-begin, and the "names" module will provide
> only my-begin.
>
> --Carl
>
> On Tue, Aug 23, 2011 at 3:22 AM, Maxim Romashchenko<max at anahoret.com>  wrote:
>> Thank you, Matthias and Carl, for a detailed reply.
>>
>> I'm now much close to my goal. Almost there.
>>
>> The approach with non-hygienic macro (item 6 in your email) works, but it
>> requires each symbol to be introduced explicitly (twice, with two different
>> names -- f and ff in your example).
>>
>> On the other hand, local-require (item 4) accomplishes almost the same thing
>> in a much clear way.
>>
>> (I'm not yet familiar with units, so I did not fully understand item 5, but
>> I assume it does essentially the same thing as local-require, but using
>> define-values/invoke-unit.)
>>
>> I wonder is there a way to hide require-local inside a macro, so it will be
>> implicit? In other words, is it possible to keep the user code as simple as
>>
>> (module uses racket
>>    (require 'names)
>>    (my-names-begin
>>      (f)))
>>
>> but still to be able to access all the symbols from the module inside the
>> my-name-begin form, as if there is require-local call?
>>
>> Thank you.
>>
>> Best regards, Maxim.
>>
>>
>> On 2011-08-22 18:05, Matthias Felleisen wrote:
>>>
>>> Maxim, let me re-order Carl's message.
>>>
>>> 1. You are trying to implement a non-hygienic macro. That is, your
>>> my-begin macro is supposed to bind names in its expressions that are
>>> not in the scope of the expressions.
>>>
>>> 2. Racket's macro system is hygienic, that is, the default does not
>>> allow such scope manipulations. The assumption behind this default is
>>> that such manipulations are usually faulty and introduce subtle (as
>>> in difficult to find) errors.
>>>
>>> 3. As the word 'default' suggests, there are ways around this
>>> restriction -- but they tell the programmer to be careful. [[
>>> Surprisingly, it also means that the seemingly-overly-restrictive
>>> hygienic macro system has more expressive power than the plain Lisp
>>> macro system. ]]
>>>
>>> 4. Since we don't quite understand your actual goal, Carl lists a
>>> number of alternatives. One is to locally require a module into a
>>> scope definition context:
>>>
>>> #lang racket/load
>>>
>>> (module names racket/base
>>>    (provide f)
>>>    (define (f) (displayln "hello world")))
>>>
>>> (module uses racket
>>>    (require 'names)
>>>    (let ()
>>>      (local-require 'names)
>>>      (f)))
>>>
>>> (require 'uses)
>>>
>>> But, as it turns out to my surprise, local-require pollutes the global
>>> scope:
>>>
>>> #lang racket/load
>>>
>>> (module names racket/base
>>>    (provide f)
>>>    (define (f) (displayln "hello world")))
>>>
>>> (module uses-bad racket
>>>    (require 'names)
>>>    (f)
>>>    (let ()
>>>      (local-require 'names)
>>>      (f)))
>>>
>>> (require 'uses-bad)
>>>
>>> Perhaps I just misunderstand.
>>>
>>> 5. An alternative is to define a unit (a first-class module), to export it
>>> and its signature, and to splice its definitions into a local scope, like
>>> this:
>>>
>>> #lang racket/load
>>>
>>> (module a racket
>>>    (provide a@ a^)
>>>    (define-signature  a^ (f))
>>>    (define a@ (unit (import) (export a^) (define (f) (displayln "hello
>>> world")))))
>>>
>>> ;; testing the unit splice
>>> (module c racket
>>>    (require 'a)
>>>    (let ()
>>>      (define-values/invoke-unit a@ (import) (export a^))
>>>      (f)))
>>>
>>> ;; testing its locality
>>> (with-handlers ((exn:fail:syntax? (lambda (x)
>>>                                      (define faulty-expressions
>>> (exn:fail:syntax-exprs x))
>>>                                      (define faulty-name (map syntax-e
>>> faulty-expressions))
>>>                                      (displayln `(,(exn-message x)
>>> , at faulty-name)))))
>>>    (eval '(module c racket
>>>             (require 'a)
>>>             (let ()
>>>               (define-values/invoke-unit a@ (import) (export a^))
>>>               (f))
>>>             f)))
>>>
>>> (require 'c)
>>>
>>> 6. Last but not least, you could define an unhygienic macro like this:
>>>
>>> #lang racket/load
>>>
>>> (module names racket
>>>    (provide (rename-out (my-begin begin)))
>>>
>>>    (require (for-syntax syntax/parse))
>>>
>>>    (define (f) (displayln "hello world"))
>>>    (define-syntax (my-begin stx)
>>>      (syntax-parse stx
>>>        ((_ body:expr ...)
>>>         (let ((ff (datum->syntax stx 'f)))  ;; breaking hygiene
>>>           #`(let ()
>>>               (define #,ff f)
>>>               body
>>>               ...))))))
>>>
>>>
>>> (module uses racket
>>>    (require 'names)
>>>    (begin
>>>      (f)))
>>>
>>> (require 'uses)
>>>
>>
>>>
>>>
>>> On Aug 22, 2011, at 10:23 AM, Carl Eastlund wrote:
>>>
>>>> Maxim,
>>>>
>>>> There are a few tools that might accomplish what you want.  To have
>>>> scoped definitions available to a set of top-level definitions (i.e.
>>>> those inside my-begin), use splicing-local from racket/splicing.  To
>>>> make a set of definitions available at one place, you could package
>>>> them up as a unit and use define-values/invoke-unit, or as a package
>>>> and use open-package, or as a module (separate from the one with
>>>> my-begin) and use local-require.  In all of these cases, the binding
>>>> of those forms is unhygienic (you are binding names that are not given
>>>> by the user of the my-begin macro), so you may have to do some direct
>>>> manipulation of syntax objects to get the scope how you want it.  I
>>>> hope I've at least given you some useful starting places.
>>>>
>>>> Carl Eastlund
>>>>
>>>> On Mon, Aug 22, 2011 at 10:04 AM, Maxim Romashchenko<max at anahoret.com>
>>>>   wrote:
>>>>>
>>>>> Hello.
>>>>>
>>>>> Thank you for your reply, Eli.
>>>>> It looks like I need to state my question more clearly.
>>>>>
>>>>> The trick I'm looking for is how to create a quasi-begin form inside
>>>>> which
>>>>> you can use all the other symbols defined in the module, while those
>>>>> symbols
>>>>> are not imported into top-level. In fact the only thing added to the top
>>>>> level is supposed to be the quasi-begin form itself.
>>>>>
>>>>> Best regards, Maxim.
>>>>>
>>>>>
>>>>> On 2011-08-22 16:04, Eli Barzilay wrote:
>>>>>>
>>>>>> 50 minutes ago, Maxim Romashchenko wrote:
>>>>>>>
>>>>>>> --- my-module.rkt ---
>>>>>>> #lang racket
>>>>>>> (provide my-begin)
>>>>>>>
>>>>>>> (define foo
>>>>>>>     ...
>>>>>>> ---------------------
>>>>>>
>>>>>> You could do this:
>>>>>>
>>>>>>    #lang racket
>>>>>>    (provide (rename-out [begin my-begin]))
>>>>>>
>>>>>> and get what you want,
>>>>>>
>>>>>>> --- main.rkt ---
>>>>>>> #lang racket
>>>>>>> (require "my-module.rkt")
>>>>>>>
>>>>>>> (my-begin
>>>>>>>     (foo
>>>>>>>       ...
>>>>>>> -----------------
>>>>>>
>>>>>> but it's probably easier to do this instead here:
>>>>>>
>>>>>>    #lang racket
>>>>>>    (require (rename-in racket [begin my-begin]))



Posted on the users mailing list.