Вопрос о clojure пространствах имен и макросах

Предположим, что у меня есть набор пространств имен (яблоко, банан, оранжевый). В этих пространствах имен я использую eat макрос, который звонит (не "генерирует", вызовы), peel функция. peel функция отличается для каждого фрукта, но макросы являются идентичными, и довольно большими, таким образом, я хотел бы создать a fruit пространство имен, которое содержит eat макрос. Но когда я звоню eat макрос от apple пространство имен, eat макрос должен звонить apple/peel функция.

Для иллюстрирования (но это не работает):

(ns fruit)
(defmacro eat [] (peel))

(ns apple)
(defn peel [] (prn "peeled apple"))
(fruit/eat)

(ns banana)
(defn peel [] (prn "peeled banana"))
(fruit/eat)

Для подчеркивания это означает, что функция кожицы должна быть вызвана, когда, и только когда, макрос расширен, как в этом примере.

(ns apple)
(defn peel [] (prn "peeled apple"))
(defmacro eat [] (peel))
(macroexpand-1 '(eat))

Так, какие-либо идеи о том, как объединить макросы и полиморфизм?

6
задан Michiel de Mare 2 March 2010 в 15:11
поделиться

4 ответа

(defmacro eat [] ((var-get (resolve 'peel))))

Обратите внимание, что вы злоупотребляете пространствами имен.

2
ответ дан 8 December 2019 в 18:35
поделиться

EDIT: Извините. Я уже опубликовал следующее. Но вы говорите "вызывает, а не генерирует" функцию peel. Так что то, что я написал, вероятно, не то, что вы хотите, хотя кажется, что это даст желаемый результат.

Простое цитирование (peel) сработало для меня.

(ns fruit)
(defmacro eat [] '(peel))

(ns apple)
(defn peel [] (prn "peeled apple"))
(fruit/eat)

(ns banana)
(defn peel [] (prn "peeled banana"))
(fruit/eat)
1
ответ дан 8 December 2019 в 18:35
поделиться

Вы описываете не полиморфизм, а то, что называется локальным захватом . Вы хотите, чтобы макрос eat «захватил» локальное определение peel .

Это считается плохим стилем в большинстве Lisp, особенно в Clojure, поскольку он может привести к незаметным и непредсказуемым ошибкам.

Лучшее решение - передать правильный peel макросу eat , когда вы его вызываете:

(ns fruit)
(defmacro eat [peeler] `(~peeler))

(ns apple)
(defn peel [] (prn "Peeled an apple"))
(fruit/eat peel)

Если вы действительно хотите выполнить локальный захват, вы можете принудительно выполнить его с помощью ~ '(без кавычек) в макросе:

(ns fruit)
(defmacro eat [] `(~'peel))
7
ответ дан 8 December 2019 в 18:35
поделиться

Как объясняется в отредактированном вопросе, это немного отличается от локального захвата, потому что вы не используете peel в макрорасширении, а скорее в выполнении самого макроса.

Это сложно, потому что макросы не оценивают свои аргументы. Даже если вы передадите peel в качестве аргумента для eat , в теле макроса это будет просто символ, а не вызываемая функция.

Единственный способ сделать то, что вы хотите (без использования eval ), - это разрешить символ во время компиляции:

(defmacro eat []
   ((var-get (resolve 'peel)))
   ... return the expansion of "eat" ...)

Функция resolve принимает символ и возвращает Var он отображается в текущем пространстве имен. Когда у вас есть Var, вы можете получить фактическую функцию (значение Var) с помощью var-get . Дополнительный набор круглых скобок вызывает эту функцию.

Излишне говорить, что это очень необычный дизайн и может потребовать переосмысления.

3
ответ дан 8 December 2019 в 18:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: