The answer that @soegaard gave is really complete enough, but for the
sake of people who will look for just a more common-looking template
system, here's one way to do that.
The main thing is to remember that @-forms are just another way of
writing Racket code, so we're really looking for a general way of
replacing names based on a given hash-table. (Since Racket has tons of
ways to do that, there are tons of ways to do that with @-forms.)
This one uses a lookup function L
that looks up values in a hash table
that is kept in a paremeter. Since this parameter is "live" only when
rendering the text, it actually produces thunks to delay the lookup
until the text is rendered. (I modified the hash table a bit to hold
symbols for more convenient keys.) It uses the output
function from
scribble/text
to produce the result allowing many kinds of values in
the template (like nested lists). For the same reason, there is no need
to try and use a string for the result, it's just a list of things.
Then, with-output-to-string
is used to collect the text into a string.
#lang at-exp racket
(require scribble/text)
(define current-replacements (make-parameter #f))
(define (L key) (λ() (hash-ref (current-replacements) key)))
(define (render-with-hash ht template)
(parameterize ([current-replacements ht])
(with-output-to-string (λ() (output template)))))
(define ht (make-hash '([Name . "Simon"])))
(define template @list{Hello @L['Name]})
(render-with-hash ht template) ; => "Hello Simon"
A slightly more convenient variation is to use a macro for L
which
makes the quoting redundant:
...
(define-syntax-rule (L key) (λ() (hash-ref (current-replacements) 'key)))
...
(define template @list{Hello @L[Name]})
...
... or, since {}
s are just @-syntax for strings, go back to using
string for the hash keys:
#lang at-exp racket
(require scribble/text)
(define current-replacements (make-parameter #f))
(define (L key) (λ() (hash-ref (current-replacements) key)))
(define (render-with-hash ht template)
(parameterize ([current-replacements ht])
(with-output-to-string (λ() (output template)))))
(define ht (make-hash '(["Name" . "Simon"])))
(define template @list{Hello @L{Name}})
(render-with-hash ht template) ; => "Hello Simon"
One caveat to keep in mind here is that the {}
s can be a few strings,
for example, if there's a newline in the textual content. If you want
to deal with that, you can adjust the L
function to accept multiple
arguments and append them together, then normalize the spaces before the
lookup is done:
#lang at-exp racket
(require scribble/text)
(define current-replacements (make-parameter #f))
(define (L . keys)
(λ() (hash-ref (current-replacements)
(regexp-replace #px"\\s+" (string-append* keys) " "))))
(define (render-with-hash ht template)
(parameterize ([current-replacements ht])
(with-output-to-string (λ() (output template)))))
(define ht (make-hash '(["First Name" . "Simon"])))
(define template @list{Hello @L{First
Name}})
(render-with-hash ht template) ; => "Hello Simon"
One thing that is a bit awkward in all of these is the use of a
parameter holding a hash table. Something like this is needed only when
you don't know the keys that are used in advance. In most cases you do,
and for that you can just use plain variables as arguments to the
template that becomes a simple function:
#lang at-exp racket
(require scribble/text)
(define (template Name)
@list{Hello @Name})
(with-output-to-string (λ() (output (template "Simon"))))
; => "Hello Simon"
One last side note: I used output
in all of these things so you can
have nested structures of things in the text. If all you need is just a
bunch of strings, you can use string-append
:
#lang at-exp racket
(define (template Name)
@string-append{Hello @Name})
(template "Simon") ; => "Hello Simon"
Or, as in @soegaard's answer, use the ~a
function, which is kind of a
cheap version of output
(into a string) that can append a bunch of
string values (and display
ing non-string values):
#lang at-exp racket
(define (template Name)
@~a{Hello @Name})
(template "Simon") ; => "Hello Simon"