Предположим, что у меня есть набор пространств имен (яблоко, банан, оранжевый). В этих пространствах имен я использую 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))
Так, какие-либо идеи о том, как объединить макросы и полиморфизм?
(defmacro eat [] ((var-get (resolve 'peel))))
Обратите внимание, что вы злоупотребляете пространствами имен.
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)
Вы описываете не полиморфизм, а то, что называется локальным захватом . Вы хотите, чтобы макрос 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))
Как объясняется в отредактированном вопросе, это немного отличается от локального захвата, потому что вы не используете peel в макрорасширении, а скорее в выполнении самого макроса.
Это сложно, потому что макросы не оценивают свои аргументы. Даже если вы передадите peel в качестве аргумента для eat , в теле макроса это будет просто символ, а не вызываемая функция.
Единственный способ сделать то, что вы хотите (без использования eval ), - это разрешить символ во время компиляции:
(defmacro eat []
((var-get (resolve 'peel)))
... return the expansion of "eat" ...)
Функция resolve принимает символ и возвращает Var он отображается в текущем пространстве имен. Когда у вас есть Var, вы можете получить фактическую функцию (значение Var) с помощью var-get . Дополнительный набор круглых скобок вызывает эту функцию.
Излишне говорить, что это очень необычный дизайн и может потребовать переосмысления.