Почему вы не можете использовать кавычки в обычном (без кавычках) коде? - PullRequest
5 голосов
/ 07 февраля 2012

В Clojure вы можете отменить кавычки и объединить список значений для генерации кода, например,

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

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

Кажется логичным, что тот же подход должен работать в контексте без кавычек, например,

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

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

Есть ли глубокая техническая / философская причина, почему это не может работать?

Ответы [ 2 ]

13 голосов
/ 07 февраля 2012

Одна простая причина состоит в том, что тогда

`(+ 100 200 ~@extra-values) 

будет плохо определено: оно расширится до

(+ 100 200 1 2 3 4)

или до

(+ 100 200 ~@extra-values)

?Оба являются допустимыми интерпретациями, если конструкция ~@ является допустимой в этом контексте.

Это также вызывает серьезные проблемы в макросистеме.Рассмотрим

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

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

Как foo может знать, какие аргументы передаются?Это, конечно, не может расширить это во время компиляции, так что теперь объединение без кавычек допустимо только в некоторых контекстах без кавычек.плохое понимание основных понятий - если вы действительно хотите без кавычек создать сложный список аргументов, вы можете легко использовать apply в чем-то вроде

(apply + `(100 ~@extra-values 200))
2 голосов
/ 09 февраля 2012

Смысл syntax-quote, unquote и unquote-splicing заключается в том, чтобы помочь разработчику писать макросы.

Например, без syntax-quote и unquote вам придется написать

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

вместо:

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

В первом случае читателю (читателю-человеку, а не реплею) труднее понять, как будет выглядеть получающаяся форма,тогда как в последнем случае сохраняется «форма» результирующей формы.

Так что, если вместо vector [1 2 3 4] мы хотим объединить содержимое extra-values как элементы в результирующую форму?Нам нужно unquote-splicing, чтобы мы могли написать:

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

вместо:

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

И снова версия unquote-splicing позволяет коду напоминать «форму» получающегосяформируется при вычислении кода, тогда как в последней версии «форма» теряется в шуме apply и list.

Оба эти примера очень просты, но syntax-quote и друзья действительноприходят сами по себе при написании более сложных макросов.

Возвращаясь к вашему вопросу о том, почему вы не можете написать (+ 1000 ~@extra-values)?У нас уже есть эта функциональность в apply (с некоторыми дополнительными ограничениями):

user=> (apply + 1000 extra-values)
1010
...