[racket] hygienic read-macros (was: module-level definitions vs. local definitions and how binding works)

From: Alexander D. Knauth (alexander at knauth.org)
Date: Mon Jul 28 13:51:12 EDT 2014

On Jul 28, 2014, at 11:41 AM, Roman Klochkov <kalimehtar at mail.ru> wrote:

> I tried to make it with module
> (module m racket
>  (define-syntax (make-reader stx)
>    (syntax-case stx ()
>      [(_ NAME BODY ...)
>       (with-syntax ([GENSYM (datum->syntax stx (gensym))])
>         #'(begin
>             (define-syntax (GENSYM stx)
>               (syntax-case stx ()
>                 [(_ in src line col pos) BODY ...]))
>             (provide GENSYM)
>             (define NAME
>               (case-lambda
>                 [(ch in)
>                  (datum->syntax #f '(GENSYM in #f #f #f #f))]
>                 [(ch in src line col pos)
>                  (datum->syntax #f '(GENSYM in src line col pos))]))))]))
>  (make-reader read-dollar
>               #'(lambda (x) x))
> 
>  (current-readtable 
>   (make-readtable (current-readtable)
>                   #\$ 'terminating-macro read-dollar)))
> 
> (require 'm)
> 
> Works just fine

Yes because GENSYM was provided by ‘m, but if it’s a #lang then that doesn’t matter unless you specifically require experiment-with-hygenic-reader-extensions/lang/reader.  And as I say below, I shouldn’t have to do that, and I shouldn’t have to provide GENSYM either.  

> I'm not very proficient with lang, but you should require module with `make-reader' to you last module.
> 
> I installed your package via `raco pkg install git://github.com/AlexKnauth/experiment-with-hygenic-reader-extensions', but
> 
> $ /usr/racket/bin/racket try-it.rkt
> default-load-handler: cannot open module file
>  module path: experiment-with-hygenic-reader-extensions/lang/reader
>  path: /home/monk/.racket/6.0.1/pkgs/experiment-with-hygenic-reader-extensions/experiment-with-hygenic-reader-extensions/lang/reader.rkt
>  system error: No such file or directory; errno=2
> 
> Maybe something wrong.

Hmm, it doesn't do that for me.  
But I’m just wondering, when you look at …/pkgs/experiment-with-hygenic-reader-extensions, does it have …extensions/langreader.rkt instead of …extensions/lang/reader.rkt ?  

> I think you should add (require (submod experiment-with-hygenic-reader-extensions/lang/reader reader)) to try-it. Or make another module with make-reader, which will be required by lang and result program.

Well another good thing about hygienic macros and syntax-objects is encapsulation so that a macro can use GENSYM without having to provide GENSYM, so that GENSYM does not have visible to the user of the macro.  So you shouldn’t have to add a (require experiment-…/lang/reader) to try-it.rkt.

But for some reason that’s not always working for reader extensions.  

It seems to work when the binding is from racket/base and there’s no local binding conflicting with it (but module-level bindings conflicting with it are ok), but not when it’s from a different module other than racket/base?  

I don’t know.  

> 
> Mon, 28 Jul 2014 11:08:20 -0400 от "Alexander D. Knauth" <alexander at knauth.org>:
>> Well that works fine when it’s in the same module (or namespace), but when I put it in a #lang, it breaks and I get this error:
>> experiment-with-hygenic-reader-extensions/try-it.rkt:
>> #lang experiment-with-hygenic-reader-extensions
>> $
>> ; g15667: unbound identifier in module in: g15667
>> 
>> That’s one problem with unhygienic reader extensions. 
>> Since you used (datum->syntax #f ‘(GENSYM …)), it eventually took on the lexical context of try-it.rkt, where the GENSYM name isn’t there.
>> 
>> Though strangely, if I put some lexical context on GENSYM by using (datum->syntax #f `(,#'GENSYM …)), then it gives me this error:
>> experiment-with-hygenic-reader-extensions-2/try-it.rkt:
>> #lang experiment-with-hygenic-reader-extensions-2
>> $
>> ; require: namespace mismatch;
>> ; reference to a module that is not available
>> ;  reference phase: 0
>> ;  referenced module: “…/experiment-with-hygenic-reader-extensions-2/lang/reader.rkt"
>> ;  referenced phase level: 0 in: g24
>> 
>> https://github.com/AlexKnauth/experiment-with-hygenic-reader-extensions
>> 
>> On Jul 28, 2014, at 3:49 AM, Roman Klochkov < kalimehtar at mail.ru > wrote:
>> 
>>> 
>>> You may use such macro for making hygienic read-macros
>>> 
>>> (define-syntax (make-reader stx)
>>>   (syntax-case stx ()
>>>     [(_ NAME BODY ...)
>>>      (with-syntax ([GENSYM (datum->syntax stx (gensym))])
>>>        #'(begin
>>>            (define-syntax (GENSYM stx)
>>>              (syntax-case stx ()
>>>                [(_ in src line col pos) BODY ...]))
>>>            (provide GENSYM)
>>>            (define NAME
>>>              (case-lambda
>>>                [(ch in)
>>>                 (datum->syntax #f '(GENSYM in #f #f #f #f))]
>>>                [(ch in src line col pos)
>>>                 (datum->syntax #f '(GENSYM in src line col pos))]))))]))
>>> 
>>> Usage example:
>>> (make-reader read-dollar
>>>              #'(lambda (x) x))
>>> 
>>> (current-readtable 
>>>  (make-readtable (current-readtable)
>>>                  #\$ 'terminating-macro read-dollar))
>>> 
>>> Test:
>>>> $
>>> #<procedure>
>>>> (let ([lambda 1]) $)
>>> #<procedure>
>>> 
>>> All works just fine
>>> 
>>> Sun, 27 Jul 2014 20:10:48 -0400 от "Alexander D. Knauth" < alexander at knauth.org >:
>>>> It seems like “hygienic reader extensions” still work when a module-level definition conflicts with it, but it seems like if it’s a local binding the local binding overrides the one in the reader extension. 
>>>> 
>>>> For example:
>>>> #lang rackjure
>>>> ;; this works:
>>>> (define lambda "module-level-whatever")
>>>> #λ(void %1) ; #<procedure>
>>>> ;; this doesn't work
>>>> (let ([lambda "local-whatever"]
>>>>      [%1 void]
>>>>      [define-syntax void]
>>>>      [% void])
>>>>  #λ(void %1)) ; application: not a procedure;
>>>> ;              ;   expected a procedure that can be applied to arguments
>>>> ;              ;   given: "local-whatever"
>>>> ;              ;   arguments…:
>>>> 
>>>> Why does it work at the module-level and not within a let? 
>>>> 
>>>> Is this a bug, or should reader extensions not be used this way, or what? 
>>>> 
>>>> What’s going on here?
>>>> 
>>>> 
>>>> ____________________
>>>>  Racket Users list:
>>>> http://lists.racket-lang.org/users
>>>> 
>>> 
>>> 
>>> -- 
>>> Roman Klochkov
>> 
> 
> 
> -- 
> Roman Klochkov



Posted on the users mailing list.