Closed jaggedblades closed 9 months ago
This issue arises from how fennel handles multiple values being passed to an operator via any multi value return.
From the Fennel Lang Reference [Operators] take any number of arguments, as long as that number is fixed at compile-time. For instance, (= 2 2 (unpack [2 5])) will evaluate to true because the compile-time number of values being compared is 3. Multiple values at runtime will not be taken into account.
In this example two-values
returns the strings just as unpack would from a table, however the ..
operation doesn't work as expected.
(fn two-values []
(values "Hello " "World!"))
(.. (two-values))
;;> "Hello " "World!"
(.. (two-values) :1)
;;> "Hello 1"
Only the first return value is passed into the function. As the reference guide says, this behaviour applies to all built in operations (+
, -
, ..
etc).
So in your case where your are trying to unpack string into the ..
operator, the total count of arguments known at compile time is only 1 so it only takes the first argument.
(macro concat-1 [& str]
(.. (unpack str)))
(print (concat-1 "Hello " "World!"))
print("Hello ")
The simplest way to get concat to work the way you want is to unpack at macro expand time and then concat at runtime. This means that at compile time (which falls between macro expand and runtime) the the argument count is 2 and the operation should work as expected.
(macro concat-2 [& str]
`(.. ,(unpack str)))
(print (concat-2 "Hello " "World!"))
print(("Hello " .. "World!"))
I assume you want to avoid the overhead of concating at runtime. As an alternative you can use the fennel folding function accumulate to build up your string.
(macro concat-3 [& str]
(accumulate [acc "" _i s (ipairs str)] (.. acc s)))
(print (concat-3 "Hello " "World!"))
print("Hello World!")
Ok, thank you for the solutions.