Макро-головоломка Clojure: расширяющаяся последовательность в макро-аргументах - PullRequest
4 голосов
/ 11 марта 2011

Это не мой «рабочий код», а упрощение проблемы в целях иллюстрации. Кроме того, название этого вопроса вводит в заблуждение, поскольку оно напоминает расширение ~ @, которое, как я понимаю, не всегда является проблемой. Пожалуйста, предложите лучшее название вопроса, если можете.

Дан макрос со следующей формой:

(defmacro my-add [x & ys] `(+ ~x ~@ys))

Теперь допустим, у нас есть список:

(def my-lst '(2 3))

Теперь я хочу функцию, использующую my-add, в которую я могу передать my-lst в виде аргумента, т. Е.

(call-my-add 1 my-lst)

Я определяю функцию, как кажется, очевидным образом:

(defn call-my-add [x ys]
    (apply my-add (cons x ys)))

Но:

java.lang.Exception: Can't take value of a macro: #'user/call-my-add (repl-1:60)

Я пробовал всевозможные дикие уловки, чтобы заставить функцию call-my-add работать с использованием evals, применений и даже определения call-my-add в качестве макроса, но все они дают похожие ClassCastExceptions.

Есть ли выход из этого?

Ответы [ 2 ]

6 голосов
/ 11 марта 2011

Нет. Макросы не имеют, никогда не смогут получить доступ к фактическим значениям времени выполнения , содержащимся в их аргументах, поэтому не могут их объединить в расширение. Все, что они получают, это символ (ы), который вы им передаете, в данном случае my-list. «Способ обойти это» состоит в том, чтобы определить my-add как функцию , а затем (необязательно) иметь макрос, который вызывает эту функцию для генерации ее кода.

Я написал сообщение в блоге об этом полу-недавно, что может показаться полезным.

Вы могли бы сделать это с помощью уловок, если бы захотели, но это ужасная идея почти в каждом случае:

(let [my-list '(1 2)]
  (eval `(my-add 5 ~@my-list)))
2 голосов
/ 11 марта 2011

Какой замечательный пример показывает, что макросы не являются первоклассными гражданами в Clojure (или любом известном мне Лиспе).Они не могут применяться к функциям, храниться в контейнерах или передаваться в функции и т. Д. В обмен на это они получают контроль, когда и если их аргументы оцениваются.

Что происходит при расширении макросавремя должно оставаться во времени расширения макроса.Так что если my-add оценивается во время раскрытия макроса и вы хотите использовать apply, тогда вам нужен ... другой макрос;сделать заявку.

(defmacro call-my-add [x ys]
   `(my-add ~@(cons x ys))) 

В этом смысле макросы несколько заразительны.

PS: я не на своем репле, поэтому, пожалуйста, отредактируйте, если вы видите ошибку в этом примере (или я исправлю ее, когда вернусь)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...