Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't you unquote-splice in normal (unquoted) code?

In Clojure you can unquote-splice a list of values to generate code, e.g.

(def extra-values [1 2 3 4])

`(+ 100 200 ~@extra-values)
=> (clojure.core/+ 100 200 1 2 3 4)

It seems logical that the same approach should work in an unquoted context, e.g.

(def extra-values [1 2 3 4])

(+ 1000 ~@extra-values)
=> [an error, but one could argue that the answer should be 1010??]

Is there any deep technical/philosphical reason why this can't work?

like image 531
mikera Avatar asked Feb 07 '12 03:02

mikera


2 Answers

One simple reason is that then

`(+ 100 200 ~@extra-values) 

would be poorly defined: does it expand to

(+ 100 200 1 2 3 4)

or to

(+ 100 200 ~@extra-values)

? Both are valid interpretations if the ~@ construct is legal in that context.

It also causes serious issues in the macro system. Consider

(defmacro foo [x & args]
  `(list ~x ~(count args)))

(let [data '(1 2 3)]
  (foo "three" ~@data))

How can foo know what args it is being passed? It certainly can't expand that at compile-time, so now unquote-splicing is only valid in some non-quoted contexts.

And overall it's just muddying up the language to accommodate for poor understanding of core concepts - if you really want to unquote-splice to build a complicated argument list, you can easily use apply in something like

(apply + `(100 ~@extra-values 200))
like image 174
amalloy Avatar answered Sep 28 '22 02:09

amalloy


The point of syntax-quote, unquote, and unquote-splicing is to help the developer to write macros.

For example, without syntax-quote and unquote you would have to write

user=> (list :a extra-values)
(:a [1 2 3 4])

instead of:

user=> `(:a ~extra-values)
(:a [1 2 3 4])

In the former case it is harder for the reader (human reader - not the repl) to understand what the resulting form is going to look like, whereas the latter case maintains the 'shape' of the resulting form.

So what if instead of the vector [1 2 3 4] we want to splice the contents of extra-values as elements into the resulting form? We need unquote-splicing so that we can write:

user=> `(+ 100 200 ~@extra-values)
(clojure.core/+ 100 200 1 2 3 4)

instead of:

user=> (concat `(+ 100 200) extra-values)
(clojure.core/+ 100 200 1 2 3 4)

Again the unquote-splicing version allows the code to resemble the 'shape' of the resulting form when the code is evaluated, whereas in the latter version the 'shape' gets lost in the noise of apply and list.

Both these examples are very simple, but syntax-quote and friends really come to their own when writing more complicated macros.

Coming back to your question of why you can't write (+ 1000 ~@extra-values)? We already have that functionality in apply (with a few more limitations):

user=> (apply + 1000 extra-values)
1010
like image 21
liwp Avatar answered Sep 28 '22 02:09

liwp