[racket] Scoped require

From: Carl Eastlund (cce at ccs.neu.edu)
Date: Tue Aug 23 08:03:18 EDT 2011

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.