Unquoting without namespace
Asked Answered
V

3

6

I need to quote without namespace and combine it with unquoting. Something like:

'[a b ~c]

Unfortunately, unquoting works only with syntactic quoting:

`[a b ~c]

But then it expands to

[user/a user/b 7]

I would like to expand without namespaces.

Varnado answered 29/4, 2020 at 2:20 Comment(0)
V
7

What was suggested on clojurians slack channel is as follows:

Use a combination of "quote unquote" for symbols to get rid of namespaces:

`[~'a ~'b ~c]

and this works perfectly.

Varnado answered 29/4, 2020 at 2:20 Comment(0)
A
0

As a reference, I have been working on a similar capability that doesn't require defensive treatment like ~'a for each symbol you wish to remain unchanged. It isn't published yet, but here is the technique:

 ;-----------------------------------------------------------------------------
 (defn unquote-form?
   [arg]
   (and (list? arg)
     (= (quote unquote) (first arg))))

 (defn unquote-splicing-form?
   [arg]
   (and (list? arg)
     (= (quote unquote-splicing) (first arg))))

 (defn quote-template-impl
   [form]
   (walk/prewalk
     (fn [item]
       (cond
         (unquote-form? item) (eval (xsecond item))
         (sequential? item) (let [unquoted-vec (apply glue
                                                 (forv [it item]
                                                   (if (unquote-splicing-form? it)
                                                     (eval (xsecond it))
                                                     [it])))
                                  final-result (if (list? item)
                                                 (t/->list unquoted-vec)
                                                 unquoted-vec)]
                              final-result)
         :else item))
     form))

 (defmacro quote-template
   [form]
   (quote-template-impl form))

and unit tests to show it in action:

 ;-----------------------------------------------------------------------------
 (def vec234 [2 3 4])

 (dotest
   (is (td/unquote-form? (quote (unquote (+ 2 3)))))
   (is (td/unquote-splicing-form? (quote (unquote-splicing (+ 2 3)))))

   (is= (td/quote-template {:a 1 :b (unquote (+ 2 3))})
     {:a 1, :b 5})
   (is= (td/quote-template {:a 1 :b (unquote (vec (range 3)))})
     {:a 1, :b [0 1 2]})
   (is= (td/quote-template {:a 1 :b (unquote vec234)})
     {:a 1, :b [2 3 4]})

   (let [result (td/quote-template (list 1 2 (unquote (inc 2)) 4 5))]
     (is (list? result))
     (is= result (quote (1 2 3 4 5))))


   (is= (td/quote-template [1 (unquote-splicing vec234) 5]) ; unqualified name OK here
     [1 2 3 4 5])
   (is= (td/quote-template [1 (unquote-splicing (t/thru 2 4)) 5])
     [1 2 3 4 5])
   (is= (td/quote-template [1 (unquote (t/thru 2 4)) 5])
     [1 [2 3 4] 5])
   )

So, instead of Clojure's syntax-quote (i.e. backquote), you can use quote-template. Then, use either unquote or unquote-splicing to insert values into the quoted template without having the namespace prepended to other symbols.

Aklog answered 29/4, 2020 at 5:16 Comment(0)
T
0

I know it is probably not the answer you are looking for by why not use code to construct that thing you want? Why force it to be all done in one expression? You could for example use

(conj '[a b] c)
Trombley answered 29/4, 2020 at 7:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.