[racket] `namespace-set-variable-value!` doesn't "shadow"? How to do?
On Sun, Nov 24, 2013 at 6:54 PM, Matthew Flatt <mflatt at cs.utah.edu> wrote:
> At Sun, 24 Nov 2013 14:46:03 -0500, Greg Hendershott wrote:
>> The code below is from
>> https://github.com/greghendershott/frog/blob/f9bda203b4b26d8f24bc466db93979a015
>> 873512/frog/template.rkt
>>
>> [...]
>>
> I'm not clear on what you're trying to do,
Well, that explanation was in the "[...]" you elided. ;)
tl;dr: I'm trying to use web-server/template dynamically, and I want
the template variables to shadow any existing "global" bindings;
specifically for example the `date` function.
> but I wonder about the
> `datum->syntax` conversion to produce `to-eval`. That causes the
> expression to evaluate to have the lexical context of the module
> containing `render-template`, instead of the top-level lexical context
> of the namespace where you're evaluating the template.
>
> In other words, I think you want
>
> (define to-eval
> `(include-template ,filename))
>
> Then, passing #t as the third argument to
> `namespace-set-variable-value!` will shadow any bindings imported from
> `web-server/templates` and `frog/widgets` into the namespace.
Thank you for taking the time to make that suggestion! Unfortunately
that didn't work for me (no #%app transformer error).
After re-reading your reply, as well as the thread I mentioned [^1], I
thought about it more. I came up with the following version. It's a
mix of insights I got from Robby and Matthew Butterick's conversation
about speed, plus going back to Jay's original example that, if you
want to shadow something... simply use `let`.
This is the cleanest version I've seen so far (for me, at my level of
understanding) and it performs as fast as what I had before:
--- template.rkt ---
(require web-server/templates
frog/widgets)
;; The modules needed by the template. Note that these must
;; be required above normally in this template.rkt module.
(define mods '(racket
web-server/templates
frog/widgets))
(define/contract (render-template dir filename dict)
(path? path-string? dict? . -> . string?)
(define orig-ns (current-namespace))
(parameterize ([current-namespace (make-base-empty-namespace)]
[current-load-relative-directory dir])
;; `namespace-attach-module` says the new namespace can
;; reuse the module already imported into orig-ns. Faster.
(for-each (curry namespace-attach-module orig-ns) mods)
;; Require the files into the namespace, too. In the case
;; of racket, that's mandatory (sorta the #lang racket);
;; the others we could require in the eval form, but may
;; as well do here, too.
(for-each namespace-require mods)
;; And finally, eval a form:
(eval `(begin
;; The variables from `dict`. The `let` will shadow
;; anything already defined, e.g. `date`.
(let (,@(for/list ([(k v) (in-dict dict)])
(list k v)))
(include-template ,filename)))
(current-namespace))))
(provide render-template)
---
So I wanted to share that in case it's useful for anyone else.
[1]: http://www.mail-archive.com/users@racket-lang.org/msg18108.html