[racket] Scoped require

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Mon Aug 22 11:05:48 EDT 2011

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.