1 # A discussion of parts of the design and trade-offs in this library
3 ## Terminology and semantics
5 - `Hello, {thing}!` is a template string.
6 - In that,
`{thing}` is a template region.
7 - In that,
`thing` is a key.
8 - `{{` is an escaped opening curly brace.
9 - `}}` is an escaped closing curly brace.
11 Beyond that, these are *suggestions* for template region semantics:
13 - In the
`{post.date:short}` template region:
14 - `post.date` is a path (the
`date` field within the
`post` object);
15 - `short` is a format specifier, which we might here imagine to mean “use
16 localised date formatting, short form”.
18 - In the
`{post.date time hour=24}` and
`{post.date:time,hour=24}`
19 template regions (I’m not suggesting particular characters):
20 - `post.date` is a path (the
`date` field within the
`post` object);
21 - `time` is a boolean property, and
`hour` a property with value
`24`,
22 which we might here imagine to mean “show the time, in
24-hour mode”.
24 ## Explanation of escaping
26 It is necessary that literal
`{` and
`}` be able to be expressed, even if
27 they’re rare; there are three common techniques for achieving this:
29 1. **Reserve certain template region keys to signify the delimiters,** e.g.
30 `{open_curly}` and
`{close_curly}` to expand to
`{` and
`}`. Downsides: may
31 clash with real template region keys when arbitrary user keys are supported;
32 and it’s generally less memorable.
33 2. **Double the delimiter:**
`{{` expands to
`{` and
`}}` to
`}`. Seen in
34 string delimiters in a few languages, so
`"foo""bar"` means “foo"bar”.
35 Downside: makes including the delimiters inside the key a difficult
36 proposition (see below).
37 3. **Escape by prefixing with another selected character:** select an escape
38 character which is also rare, most commonly a backslash due to its
39 convention in programming languages. Problems with using backslash: if you
40 need to represent Windows paths, literal backslashes aren’t so rare after
41 all, and you need to escape them all; and if you need to encode the template
42 string in a string literal in another language that uses the same escape
43 character (e.g. a TOML config file), all backslashes must now be escaped
44 again, e.g. string literals
`"C:\\\\dir"` or
`"\\{ ← literal curly"`.
46 (A fourth technique that can be employed is delimiting the replacement regions
47 with values that will not appear in the desired text, as with the ␜, ␝, ␞ and ␟
48 separator control characters in ASCII—and that more or less failed because it
49 was roughly machine-writable only, and so file types like CSV prospered due to
50 being human-writable, despite the acconpanying escaping woes. This language
51 requires that the delimiters be easy for the user to type, so this fourth
52 technique is deemed impossible—all values that are easy to type may occur in
55 One interesting consideration is whether you can express the delimiters inside
56 template regions. For simplicity, I’ve currently banned curlies inside template
57 regions, but there are things to consider if it becomes desirable to express it
58 later (e.g. in a format specifier). Here’s how each of the three techniques
59 previously mentioned handles it:
61 1. Solvable in two ways, both of which require that curlies be paired inside
63 (a) Replace only
`{open_curly}` and
`{close_curly}` literally inside
65 (b) Perform recursive template resolution!
67 2. Well, should
`{foo}}}` mean “template region with key ‘foo’ followed by
68 literal ‘}’”, or “template region with key ‘foo}’”? If you wish both to be
69 expressible, you need to provide some means of disambiguation. The most
70 straightforward is to ban a certain character at the start and end of
71 template regions, most probably HORIZONTAL SPACE (that is: declare “no
72 leading or trailing whitespace”), and then repurpose that to behave
73 essentially the same as
`#` on Rust’s raw string literals, where you can
74 write
`r"…"`,
`r#"…"`,
`r##"…"##`, *&c.* With this,
`{foo}}}` would mean
75 “template region with key ‘foo’ followed by literal ‘}’”, and
`{ foo} }`
76 would mean “template region with key ‘foo}’”. (Note how escaping curlies
77 inside template regions is not required. Decide for yourself whether that’s
80 3. Allow escapes inside the template region, e.g.
`\{{\{\}}\}` would mean
81 “literal ‘{’ followed by template region with key ‘{}’ followed by literal
82 ‘}’”. Straightforward.
84 This then leads to three possibilities:
85 (a) full recursive template rendering;
86 (b) arbitrary keys, or *almost* arbitrary if leading and trailing whitespace is
87 disallowed or ignored (depending on the approach); or
88 (c) no curlies in keys please.
90 I initially implemented the third technique (escape with backslash, and allow
91 escapes in template regions), but the downsides of using backslashes became too
92 severe for my liking, and I didn’t like any alternative characters on the
93 keyboard as much as just doubling the delimiter, so I went with the second
94 technique in the end. I don’t expect its downside to bite me, but we’ll see.
96 ## Other considered syntaxes
98 I settled on using
`{…}` for template regions. I also contemplated
`$…`, but
99 ruled it for the combination of these reasons:
101 - it requires embedding UAX #
31 identifier parsing which is heavier and less
102 *definitely obvious* for real users than I’d like;
104 - for things like nested property access and format specifiers you’d need a
105 variant with a closing delimiter anyway, such as
`${…}`, so now you’d have
108 - all this makes it harder to explain all the features of the language to
111 So
`$…` was eliminated as too complex and insufficient.
113 So it became more a toss-up between
`$…$`,
`%…%`,
`${…}` and
`{…}`.
115 - `$…$` was eliminated because it’s not using any paired delimiting characters,
116 and because more programmery types would be more likely to expect
`$…` than
119 - `%…%` has precedent in Windows’ environment variables, but again the lack of
120 *paired* delimiting characters makes it not so great. Also it just feels
121 uglier in the standard sort of case I have in mind. I’m picturing something
122 like
`%ALBUM%/%TRACK% - %TITLE%.mp3`. (It doesn’t *have* to be uppercase, but
123 I find that using percent makes me think uppercase.)
125 - Given my intended purpose (*small* strings rather than big multi-line
126 strings), the
`$` in
`${…}` didn’t feel necessary: consider for example
127 `{album}/{track} - {title}.mp3`, which feels about right, compared with
128 `${album}/${track} - ${title}.mp3`, which *works*, but feels… I dunno,
129 programmery rather than usery.
133 Taken with the
`{{` and
`}}` escaping, this makes it match Rust’s format
134 strings; this is purely coincidental.
136 I’m pretty sure I considered
`[…]` at some point too, but decided I didn’t like