[racket] Scoped require

From: Maxim Romashchenko (max at anahoret.com)
Date: Tue Aug 23 03:22:00 EDT 2011

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]))
>>>>
>>> _________________________________________________
>>>   For list-related administrative tasks:
>>>   http://lists.racket-lang.org/listinfo/users
>>>
>>>
>>
>> _________________________________________________
>>   For list-related administrative tasks:
>>   http://lists.racket-lang.org/listinfo/users
>


Posted on the users mailing list.