[racket] `namespace-set-variable-value!` doesn't "shadow"? How to do?

From: Greg Hendershott (greghendershott at gmail.com)
Date: Sun Nov 24 23:48:47 EST 2013

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

Posted on the users mailing list.