See the results of phase 1 computation in phase 0 - racket

Suppose I have some module with non-trivial define "override" in Racket. That "override" collects information about the procedure-body and stores it into a map (during the compilation phase). Now I need to use the collected information during the runtime phase. The straightforward aproach doesn`t seem to work:
#lang racket
(require (for-syntax racket))
(define-for-syntax map-that-should-be-used-in-phase-0 (make-hash))
(define-for-syntax (fill-in-useful-information n) (hash-set! map-that-should-be-used-in-phase-0 n n))
; Suppose that some useful information is collected here and stored into a map
(define-syntax (fill-in-map stx)
(begin
(fill-in-useful-information 1)
(fill-in-useful-information 2)
(syntax/loc stx (displayln "OK, the map is filled, but I cannot see it here"))))
(define-syntax (print-that-map stx)
(syntax/loc stx (displayln map-that-should-be-used-in-phase-0))) ; <-- This can not be compiled
(fill-in-map)
(print-that-map)
Can I do it in Racket? If yes then how? Any hints will be greately appreciated!

An identifier referencing a variable cannot be compiled, but the value it refers to can, as long as it's one of the built-in data structures provided by Racket, and as long as it's immutable.
You can stick a hash table value into a syntax object using quasisyntax and unsyntax.
> (quasisyntax (foo #,(hash 'a 4 'b 16)))
#<syntax:5:15 (foo #hash((a . 4) (b . 16)))>
You can do the same thing to communicate one-way from compile-time to run-time.
(define-for-syntax (hash->immutable-hash hsh)
(make-immutable-hash (hash->list hsh)))
(define-syntax (print-that-map stx)
(quasisyntax/loc stx (displayln #,(hash->immutable-hash map-that-should-be-used-in-phase-0))))

Related

Racket - How to define a function that can be used both in syntax transformers and ordinary code?

I am using syntax transformers to define macros in Racket. I want to create some helper functions to help me manipulate the syntax. However, the functions I defined outside the syntax transformer are not available inside the syntax transformer. For example, in the following code
(define (my-function x) (+ x 1))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx (my-function (cadr (syntax->datum stx))))))
I got the error "my-function: reference to an unbound identifier at phase: 1; the transformer environment".
After some searching, I am able to write the following code so that my-function is available inside the syntax transformer.
(begin-for-syntax
(define (my-function x) (+ x 1)))
(provide (for-syntax my-function))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx (my-function (cadr (syntax->datum stx))))))
But the problem is, my-function is not available outside the syntax transformer this time. Sometimes I want to check those helper functions in ordinary code, so I need to be able to call it from both inside and outside the syntax transformer, just like the function cadr. How can I achieve that?
I know my question has something to do with Racket's syntax model, in particular the concept of "phase level", but I never really understand it. If you could provide some easy-to-follow tutorials explaining it I would even be more grateful.
A common way is to define your function that you want to share across phases in another (sub)module. Then, require it twice.
#lang racket
(module common racket
(provide my-function)
(define (my-function x) (+ x 1)))
(require 'common
(for-syntax 'common))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx (my-function (cadr (syntax->datum stx))))))
(my-function 1)
(my-macro 123)

Racket macro that generates a nested module error

While experimenting with racket's macros, I stumbled into a definition that wasn't at first obvious to me why it was rejected. The code is short and otherwise is probably useless, but is as follows:
#lang racket
(define-syntax (go stx)
(syntax-case stx ()
[(_ id)
#'(module mod racket
(define it id))]
))
(go 'dummy)
The complaint is quote: unbound identifier; also, no #%app syntax transformer...
If I manually inline (define it id) to (define it 'dummy) then it works.
I had a hunch that ' ie. quote of (go 'dummy) that is bound by #lang racket is not recognized as the same binding within the submodule mod even though syntactically it is the same sequence of letters. If I strip 'dummy of all lexical context by round tripping as follows:
(with-syntax ([ok (datum->syntax #f (syntax->datum #'id))])
below the pattern match (_ id) and replace definition of it with (define it ok) then all is good again.
#lang racket
(define-syntax (go stx)
(syntax-case stx ()
[(_ id)
(with-syntax ([ok (datum->syntax #f (syntax->datum #'id))])
#'(module mod racket
(define it ok)))]
))
(go 'dummy)
I presume that my dilemma was caused by the hygiene system. However, is there a more direct solution to convince racket compiler that these identifiers, ie. quote are really the same without this boilerplate?
The expression that you insert for id in:
(module mod racket
(define it id))
is going to be evaluated in the context of the module.
Therefore the syntactic context id id needs to be the same as
the context of the submodule.
You describe one way of removing existing context. Here is another:
#lang racket
(require (for-syntax racket/base))
(define-syntax (go stx)
(syntax-case stx ()
[(_ id)
(with-syntax ([id (syntax->datum #'id)])
#'(module mod racket
(provide it)
(define it id)))]))
(go 42)
(require (submod "." mod))
it
In most macros it is a good thing that context is preserved, so
having to "boiler plate" to remove it seems okay to me.
Of course, if you experience to much boiler plate then write
a macro that inserts the boiler plate for you :-)

how can I match syntax in racket?

I'd like to write a macro that matches whether the given syntax is a if or cond, I tried writing it like so:
(define-syntax (foo stx)
(syntax-case stx ()
[(_ (if a b)) #'"if!"]
[(_ (cond a b) #'"cond!"]))
but it always matches the first case. How can I distinguish between the two cases?
In a syntax-case pattern, an identifier matches anything. You already assume this to be true: when you write the pattern (if a b), presumably you assume a will match any piece of syntax, not just the literal identifier a. The same is true for the if identifier—you’re not actually checking that the identifier is if, you’re just matching any piece of syntax and binding it to a pattern variable named if.
This is what the literals list is for. When you put an identifier in the literals list, it tells syntax-case to check for that specific identifier instead of binding a pattern variable. So you probably want this, instead:
(define-syntax (foo stx)
(syntax-case stx (if cond)
[(_ (if a b)) #'"if!"]
[(_ (cond a b) #'"cond!"]))
However, note that syntax-case is pretty old, and Racket has shipped with a better syntax matching library for almost a decade now, syntax/parse. I’d recommend it over syntax-case in all situations. The syntax/parse equivalent of the above macro looks like this:
(require (for-syntax syntax/parse))
(define-syntax (foo stx)
(syntax-parse stx
#:literals [if cond]
[(_ (if a b)) #'"if!"]
[(_ (cond a b) #'"cond!"]))
…which is basically the same, but syntax-parse’s pattern language is much richer than that of syntax-case. For example, if you wanted to omit the #:literals declaration, you could annotate the individual literal patterns using ~literal:
(define-syntax (foo stx)
(syntax-parse stx
[(_ ((~literal if) a b)) #'"if!"]
[(_ ((~literal cond) a b) #'"cond!"]))
For more information, see the documentation for syntax/parse.

Module meta-language in Racket

I'm trying to write in Racket a module meta-language mylang, which accepts a second language to which is passes the modified body, such that:
(module foo mylang typed/racket body)
is equivalent to:
(module foo typed/racket transformed-body)
where the typed/racket part can be replaced with any other module language, of course.
I attempted a simple version which leaves the body unchanged. It works fine on the command-line, but gives the following error when run in DrRacket:
/usr/share/racket/pkgs/typed-racket-lib/typed-racket/typecheck/tc-toplevel.rkt:479:30: require: namespace mismatch;
reference to a module that is not available
reference phase: 1
referenced module: "/usr/share/racket/pkgs/typed-racket-lib/typed-racket/env/env-req.rkt"
referenced phase level: 0 in: add-mod!
Here's the whole code:
#lang racket
(module mylang racket
(provide (rename-out [-#%module-begin #%module-begin]))
(require (for-syntax syntax/strip-context))
(define-syntax (-#%module-begin stx)
(syntax-case stx ()
[(_ lng . rest)
(let ([lng-sym (syntax-e #'lng)])
(namespace-require `(for-meta -1 ,lng-sym))
(with-syntax ([mb (namespace-symbol->identifier '#%module-begin)])
#`(mb . #,(replace-context #'mb #'rest))))])))
(module foo (submod ".." mylang) typed/racket/base
(ann (+ 1) Number))
(require 'foo)
Requirements (i.e. solutions I'd rather avoid):
Adding a (require (only-in typed/racket)) inside the mylang module makes this work, but I'm interested in a general solution, where mylang does not need to know about typed/racket at al (i.e. if somebody adds a new language foo, then mylang should work with it out of the box).
Also, I'm not interested in tricks which declare a submodule and immediately require and re-provide it, as is done here, because this changes the path to the actual module (so main and test loose their special behaviour, for example).
It is also slower at compile-time, as submodules get visited and/or instantiated more times (this can be seen by writing (begin-for-syntax (displayln 'here)), and has a noticeable impact for large typed/racket programs.
Bonus points if the arrows in DrRacket work for built-ins provided by the delegated-to language, e.g. have arrows from ann, + and Number to typed/racket/base, in the example above.
One thing you can do, which I don't think violates your requirements, is put it in a module, fully expand that module, and then match on the #%plain-module-begin to insert a require.
#lang racket
(module mylang racket
(provide (rename-out [-#%module-begin #%module-begin]))
(define-syntax (-#%module-begin stx)
(syntax-case stx ()
[(_ lng . rest)
(with-syntax ([#%module-begin (datum->syntax #f '#%module-begin)])
;; put the code in a module form, and fully expand that module
(define mod-stx
(local-expand
#'(module ignored lng (#%module-begin . rest))
'top-level
(list)))
;; pattern-match on the #%plain-module-begin form to insert a require
(syntax-case mod-stx (module #%plain-module-begin)
[(module _ lng (#%plain-module-begin . mod-body))
#'(#%plain-module-begin
(#%require lng)
.
mod-body)]))])))
;; Yay the check syntax arrows work!
(module foo (submod ".." mylang) typed/racket/base
(ann (+ 1) Number))
(require 'foo)
And if you wanted to transform the body in some way, you could do that either before or after expansion.
The pattern-matching to insert the extra (#%require lng) is necessary because expanding the module body in a context where lng is available isn't enough. Taking the mod-body code back out of the module form means that the bindings will refer to lng, but lng won't be available at run-time. That's why I get the require: namespace mismatch; reference to a module that is not available error without it, and that's why it needs to be added after expansion.
Update from comments
However, as #GeorgesDupéron pointed out in a comment, this introduces another problem. If lng provides an identifier x and the module where it is used imports a different x, there will be an import conflict where there shouldn't be. Require lines should be in a "nested scope" with respect to the module language so that they can shadow identifiers like x here.
#GeorgesDupéron found a solution to this problem in this email on the racket users list, using (make-syntax-introducer) on the mod-body to produce the nested scope.
(module mylang racket
(provide (rename-out [-#%module-begin #%module-begin]))
(define-syntax (-#%module-begin stx)
(syntax-case stx ()
[(_ lng . rest)
(with-syntax ([#%module-begin (datum->syntax #f '#%module-begin)])
;; put the code in a module form, and fully expand that module
(define mod-stx
(local-expand
#'(module ignored lng (#%module-begin . rest))
'top-level
(list)))
;; pattern-match on the #%plain-module-begin form to insert a require
(syntax-case mod-stx (module #%plain-module-begin)
[(module _ lng (#%plain-module-begin . mod-body))
#`(#%plain-module-begin
(#%require lng)
.
#,((make-syntax-introducer) #'mod-body))]))])))

Capturing Macros in Scheme

What's the simplest way to define a capturing macro using define-syntax or define-syntax-rule in Racket?
As a concrete example, here's the trivial aif in a CL-style macro system.
(defmacro aif (test if-true &optional if-false)
`(let ((it ,test))
(if it ,if-true ,if-false)))
The idea is that it will be bound to the result of test in the if-true and if-false clauses. The naive transliteration (minus optional alternative) is
(define-syntax-rule (aif test if-true if-false)
(let ((it test))
(if it if-true if-false)))
which evaluates without complaint, but errors if you try to use it in the clauses:
> (aif "Something" (displayln it) (displayln "Nope")))
reference to undefined identifier: it
The anaphora egg implements aif as
(define-syntax aif
(ir-macro-transformer
(lambda (form inject compare?)
(let ((it (inject 'it)))
(let ((test (cadr form))
(consequent (caddr form))
(alternative (cdddr form)))
(if (null? alternative)
`(let ((,it ,test))
(if ,it ,consequent))
`(let ((,it ,test))
(if ,it ,consequent ,(car alternative)))))))))
but Racket doesn't seem to have ir-macro-transformer defined or documented.
Racket macros are designed to avoid capture by default. When you use define-syntax-rule it will respect lexical scope.
When you want to "break hygiene" intentionally, traditionally in Scheme you have to use syntax-case and (carefully) use datum->syntax.
But in Racket the easiest and safest way to do "anaphoric" macros is with a syntax parameter and the simple define-syntax-rule.
For example:
(require racket/stxparam)
(define-syntax-parameter it
(lambda (stx)
(raise-syntax-error (syntax-e stx) "can only be used inside aif")))
(define-syntax-rule (aif condition true-expr false-expr)
(let ([tmp condition])
(if tmp
(syntax-parameterize ([it (make-rename-transformer #'tmp)])
true-expr)
false-expr)))
I wrote about syntax parameters here and also you should read Eli Barzilay's Dirty Looking Hygiene blog post and Keeping it Clean with Syntax Parameters paper (PDF).
See Greg Hendershott's macro tutorial. This section uses anaphoric if as example:
http://www.greghendershott.com/fear-of-macros/Syntax_parameters.html
Although the answer above is the accepted way to implement aif in the Racket community, it has severe flaws. Specifically, you can shadow it by defining a local variable named it.
(let ((it 'gets-in-the-way))
(aif 'what-i-intended
(display it)))
The above would display gets-in-the-way instead of what-i-intended, even though aif is defining its own variable named it. The outer let form renders aif's inner let definition invisible. This is what the Scheme community wants to happen. In fact, they want you to write code that behaves like this so badly, that they voted to have my original answer deleted when I wouldn't concede that their way was better.
There is no bug-free way to write capturing macros in Scheme. The closest you can come is to walk down the syntax tree that may contain variables you want to capture and explicitly strip the scoping information that they contain, replacing it with new scoping information that forces them to refer to your local versions of those variables. I wrote three "for-syntax" functions and a macro to help with this:
(begin-for-syntax
(define (contains? atom stx-list)
(syntax-case stx-list ()
(() #f)
((var . rest-vars)
(if (eq? (syntax->datum #'var)
(syntax->datum atom))
#t
(contains? atom #'rest-vars)))))
(define (strip stx vars hd)
(if (contains? hd vars)
(datum->syntax stx
(syntax->datum hd))
hd))
(define (capture stx vars body)
(syntax-case body ()
(() #'())
(((subform . tl) . rest)
#`(#,(capture stx vars #'(subform . tl)) . #,(capture stx vars #'rest)))
((hd . tl)
#`(#,(strip stx vars #'hd) . #,(capture stx vars #'tl)))
(tl (strip stx vars #'tl)))))
(define-syntax capture-vars
(λ (stx)
(syntax-case stx ()
((_ (vars ...) . body)
#`(begin . #,(capture #'(vars ...) #'(vars ...) #'body))))))
That gives you the capture-vars macro, which allows you to explicitly name the variables from the body you'd like to capture. aif can then be written like this:
(define-syntax aif
(syntax-rules ()
((_ something true false)
(capture-vars (it)
(let ((it something))
(if it true false))))
((_ something true)
(aif something true (void)))))
Note that the aif I have defined works like regular Scheme's if in that the else-clause is optional.
Unlike the answer above, it is truly captured. It's not merely a global variable:
(let ((it 'gets-in-the-way))
(aif 'what-i-intended
(display it)))
The inadequacy of just using a single call to datum->syntax
Some people think that all you have to do to create a capturing macro is use datum->syntax on one of the top forms passed to your macro, like this:
(define-syntax aif
(λ (stx)
(syntax-case stx ()
((_ expr true-expr false-expr)
(with-syntax
((it (datum->syntax #'expr 'it)))
#'(let ((it expr))
(if it true-expr false-expr))))
((_ expr true-expr)
#'(aif expr true-expr (void))))))
Just using datum->syntax is only a 90% solution to writing capturing macros. It will work in most cases, but break in some cases, specifically if you incorporate a capturing macro written this way in another macro. The above macro will only capture it if the expr comes from the same scope as the true-expr. If they come from different scopes (this can happen merely by wrapping the user's expr in a form generated by your macro), then the it in true-expr will not be captured and you'll be left asking yourself "WTF won't it capture?"
You may be tempted to quick-fix this by using (datum->syntax #'true-expr 'it) instead of (datum->syntax #'expr 'it). In fact this makes the problem worse, since now you won't be able to use aif to define acond:
(define-syntax acond
(syntax-rules (else)
((_) (void))
((_ (condition . body) (else . else-body))
(aif condition (begin . body) (begin . else-body)))
((_ (condition . body) . rest)
(aif condition (begin . body) (acond . rest)))))
If aif is defined using the capture-vars macro, the above will work as expected. But if it's defined by using datum->syntax on the true-expr, the the addition of begin to the bodies will result in it being visible in the scope of acond's macro definition instead of the code that invoked acond.
The impossibility of really writing a capturing macro in Racket
This example was brought to my attention, and demonstrates why you just can't write a real capturing macro in Scheme:
(define-syntax alias-it
(syntax-rules ()
((_ new-it . body)
(let ((it new-it)) . body))))
(aif (+ 1 2) (alias-it foo ...))
capture-vars cannot capture the it in alias-it's macroexpansion, because it won't be on the AST until after aif is finished expanding.
It is not possible at all to fix this problem, because the macro definition of alias-it is most probably not visible from the scope of aif's macro definition. So when you attempt to expand it within aif, perhaps by using expand, alias-it will be treated as a function. Testing shows that the lexical information attached to alias-it does not cause it to be recognized as a macro for a macro definition written out of scope from alias-it.
Some would argue that this shows why the syntax-parameter solution is the superior solution, but perhaps what it really shows is why writing your code in Common Lisp is the superior solution.