[racket] Scoped require
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
>