Clojure Macro Expansion

Работя върху макрос, опитвам се да разбера как да избегна разширяването на определени форми, вземете следното и макрос например,


(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.

--Редактиране-- Имайте предвид, че горната функция също няма да разшири подформулярите на израза 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