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