Расширение макроса Clojure

Я работаю над макросом, пытаюсь понять, как избежать расширения определенных форм, например, взять следующее и макрос,


(defmacro and
  ([] true)
  ([x] x)
  ([x & next]
   `(let [and# ~x]
      (if and# (and ~@next) and#))))

При расширении,

(mexpand-all '(and 1 2 3))

становится,


(let* [and__973__auto__ 1]
      (if and__973__auto__
        (let* [and__973__auto__ 2]
              (if and__973__auto__ 3 and__973__auto__))
        and__973__auto__))

В этом случае мне нужно остановить расширение let в let*.


person Hamza Yerlikaya    schedule 13.05.2011    source источник


Ответы (3)


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

Вот краткий пример:

(defn my-expander [form]
    (cond (not (list? form)) (mexpand-1 form)
        (= (first form) 'let) form
           :else (map my-expander (mexpand-1 form))))

Пожалуйста, простите меня, если я сделал какие-либо ошибки. Я гораздо сильнее разбираюсь в Scheme и CL, чем в Clojure.

--Edit-- Обратите внимание, что приведенная выше функция также не будет расширять подчиненные формы оператора let.

person Daniel Ralston    schedule 13.05.2011

Хм? Непонятно, что вы подразумеваете под "остановкой" let расширения. let — это макрос, определенный в clojure.core, о котором компилятор ничего не знает: он понимает только let*. Если ваш макрос расширяется до let, который (каким-то образом) отказывается расширяться дальше, он не сможет скомпилироваться.

Если вы хотите изолированно проверять только вывод вашего макроса, не беспокоясь о его рекурсивном расширении, вы должны использовать macroexpand или macroexpand-1 вместо этого mexpand-all. Я не знаю, откуда взялось mexpand-all, но когда мне нужно что-то подобное, я использую clojure.walk/macroexpand-all.

person amalloy    schedule 13.05.2011
comment
То, что я пытаюсь сделать, это рекурсивно расширить все, кроме формы let. Кстати, mexpand взят из contrib macro-utils - person Hamza Yerlikaya; 13.05.2011
comment
Я тоже пытаюсь предотвратить расширение некоторых форм. Я хотел бы дать объяснение, почему эта проблема имеет право на существование. В моем случае я пытаюсь использовать расширение макроса clojure в качестве транспилера из не-clojure lisp в еще один не-clojure lisp. Он работает довольно хорошо, за исключением некоторых крайних случаев, когда синтаксис исходного lisp конфликтует со встроенными макросами clojure. - person vitaly; 10.05.2018

Используйте macroexpand-1 для выполнения одного уровня расширения макроса.

После загрузки вашего макроса and это выражение:

user=> (macroexpand-1 '(and 1 2 3))

Урожайность:

(clojure.core/let [and__1__auto__ 1] (if and__1__auto__ (clojure.core/and 2 3) and__1__auto__))
person John Cromartie    schedule 13.05.2011