How to user string interpolation in ClojureScript

Googled and found clojure.core.strint which reads "a~{b}c" and string interpolation and replaces b. Then I noticed such namespace is not available in ClojureScript.

Any other libraries I can use? Or I have to build my own?

I’m using Cuerdas library for this purpose, it also has many more useful utilities for strings https://funcool.github.io/cuerdas/latest/#interpolate

2 Likes

I looked for string interpolation when I started Clojure, mostly because I was used to it from ruby. Nowadays I think just using str is better in every way possible.

;; from cuerdas
(str/istr "value = ~(inc value)")
;; plain str
(str "value = " (inc value))

Usually it is equal or less amount of code and it doesn’t degrade when you want “longer” things interpolated.

(str "prefix"
     (->> some-seq
          (map str/upper-case)
          (str/join " ")) 
    "suffix")

You also don’t loose syntax support since no editor I know of recognizes ~() inside strings as clojure code.

Just my 2 cents.

7 Likes

There might be a situation like:

<path id="testPath" d="M3.858,58.607 c16.784-5.985,33.921-10.518,51.695-12.99c50.522-7.028,101.982,0.51,151.892,8.283c17.83,2.777,35.632,5.711,53.437,8.628 c51.69,8.469,103.241,11.438,155.3,3.794c53.714-7.887,106.383-20.968,159.374-32.228c11.166-2.373,27.644-7.155,39.231-4.449" />

and I want to interpolate all numbers in d attribute. The first time came to my mind is string interpolation. However I notices using function composition might be a better solution.

In your case, how would you do string interpolation? you have plenty of parameters, not just a fixed set like:

You have ${coins} left for a total of ${amount}

So - though I don’t know jack about SVG - I would use a clojure structure that represents a path with a function to turn it into an SVG tag by mapping and then I would have a final go to put everything together as (apply str ....).

that depends on how the macro str/istr is implemented, which I’m not sure…

I usually do this (the SVG example) using map and interpose, which I find much cleaner than string interpolation (which invents an opaque DSL inside the string).

I’m thinking about going further. Clojure checks function arities, so by using functions, I can get static checking fot those numbers. Something like https://gist.github.com/potch/4214346

Ended up with a function to assemble path data for me:

(respo.util.format/path-data :M 10 10 :l 20 20 :Z)
1 Like

You might consider something like this:

(def key->params
  {:q 4, :L 2, :M 2, :v 1, :A 7, :Q 4, :m 2, :Z 0, :T 2, :C 6, :s 4, :l 2, :z 0, :c 6, :h 1, :t 2, :H 1, :V 1, :S 4, :a 7})

(defn concat-path-data [acc xs]
  (if (empty? xs)
    acc
    (let [cursor (first xs)
          following (rest xs)
          num-params (key->params cursor)]
      (assert num-params (str "Unknown command: " cursor))
      (let [params (take num-params following)]
        (assert (and (= num-params (count params)) (every? number? params))
                (str cursor " takes " num-params " numbers"))
        (recur (str acc " " (name cursor) (string/join "," params)) (drop num-params following))))))

In general, I prefer to write a short function that uses a lookup table over a long function that repeats boilerplate. Another way to phrase that is: it’s often better to program with data than with code.

2 Likes

Exactly what I thought after woke in the morning!

1 Like