cljs-dev

borkdude 2025-12-18T12:04:18.222239Z

@dnolen Proposal for template strings in CLJS. Simply use str + tag for template fn.

(str 1 2 x) => `12${cljs.core.str.arity1(x)` ;; pseudo-code: intent is to coerce nil into empty string
^my-template-fn (str 1 2 x) => cljs.user.my_template_fn`12${x}
` Demo:
cljs.user=> (defn my-template-fn [xs & ys] (prn xs ys) (str (apply str xs) (apply str ys)))
#'cljs.user/my-template-fn
cljs.user=> (let [x 1] ^my-template-fn (str 1 2 x))
#js ["12" ""] (1)
"121"

thheller 2025-12-18T12:14:37.520089Z

The tag metadata is already used and should not get a second meaning especially for str only. Especially since its also already in use in clojure. So, I'd veto this based on that.

thheller 2025-12-18T12:15:11.997639Z

I also do not like adding this to str at all, since CLJ doesn't do that either.

thheller 2025-12-18T12:16:25.967089Z

the tag mechanism I mean. str using string templates under the hood could be fine, needs benchmarks to confirm it is generally equal or faster on all supported engines.

borkdude 2025-12-18T12:16:56.737459Z

needs benchmarks to confirm it is generally equal or faster on all supported engines.I've benchmarked this in the past and this seemed to be the case (but of course we need to do this again if we would pursue this)

borkdude 2025-12-18T12:17:59.458819Z

Just changing str to template strings doesn't really have any benefits

dnolen 2025-12-18T13:11:09.149939Z

yeah I'm not hot on adding anything to str - I agree that overloading type hints is probably too much.

dnolen 2025-12-18T13:12:11.704739Z

Are there any specific problems w/ js-template other than some verbosity? And the verbosity bit could be resolved w/ a different name.

borkdude 2025-12-18T13:20:18.817969Z

API-wise it's pretty similar to str apart from the first arg. I don't know why it needs another special form in shadow though, it can be done using a regular macro like str currently is written. Also shadow-cljs's js-template doesn't seem to inline constants other than strings which could be important for stuff like lit.

cljs.user=> (str (fn [] (js-template str 1 2 3)))
"function (){\nreturn cljs.core.str`${(1)}${(2)}${(3)}`;\n}"
although it does inline strings which is probably the most important bit:
cljs.user=> (str (fn [] (js-template str "dude" "dude" 3)))
"function (){\nreturn cljs.core.str`dudedude${(3)}`;\n}"

borkdude 2025-12-18T13:24:13.807319Z

changed my str2 macro to always take a template fn:

cljs.user=> (let [x 1] (str2 str 1 2 x))
"12,1"

cljs.user=> (str (fn [] (let [x 1] (str2 str 1 2 x))))
"function (){\nvar x = (1);\nreturn cljs.core.str`12${x}`;\n}"

borkdude 2025-12-18T13:25:06.723189Z

Maybe a good short name: strt (string template)

thheller 2025-12-19T16:19:32.576889Z

yeah definitely don't go with the js-template implementation in shadow. dunno why I went the route I went, could definitely just be a macro