'Macro string Interpolation / How to unquote inside string?
I have a macro like:
(defmacro test [x] `"~x")
After running it expands to the following:
=> (test abc)
'~x'
Why doesn't it expand to 'abc'
?
What is the correct method to unquote inside a string?
Solution 1:[1]
I'm confused why you expected this to work, and I'm unclear what you want. ~
in your example is in a string literal, so it's an ordinary string character, not the ~
operator, the same way that ((fn [x] "x") 5)
returns "x"
and not "5"
.
If you want to stringify the model that you're passing into a macro, use str
, like this:
hy 1.0a1+114.g2abb33b1 using CPython(default) 3.8.6 on Linux
=> (defmacro test [x] (str x))
<function test at 0x7fe0476144c0>
=> (test abc)
"abc"
Solution 2:[2]
I think there is a more general question here: how can I use string interpolation at macro-expansion time?
The answer is to remember that the unquote prefix ~
can be applied to any form, not just bare symbols, and that it is simply shorthand for unquote
.
So you can just use an f-string, and unquote the f-string. Using the original example:
(defmacro test [x]
(quasiquote (unquote f"{x}")))
Or using the shorthand prefix symbols:
(defmacro test [x]
`~f"{x}")
~f"{module} ..."
is valid shorthand for `(unquote f"{module} ...").
As another example of using arbitrary forms with ~
, you could use str.format
instead of an f-string:
(defmacro test [x]
`~(.format "{}" x))
As a real-world example, here is a macro I wrote today:
(import sys
typing [NoReturn])
(defn ^NoReturn die [^int status ^str message]
(print message :file sys.stderr)
(sys.exit status))
(defmacro try-import [module]
`(try
(import ~module)
(except [ImportError]
(die 1 ~f"{module} is not available, exiting."))))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Kodiologist |
Solution 2 | shadowtalker |