2016-09-07 13 views
3

Lisp-rookie здесь.Elisp: расширение макроса при времени загрузки с оценкой неопределенной переменной

Моя цель - определить макрос, который сделает ключи точечного алиста доступными как переменные для доступа к соответствующим значениям, следовательно, имя «let-dotted-alist». Так что я хочу:

(setq foo '((a . "aa") (b . "bb"))) 

(let-dotted-alist foo 
(message (concat a b))) 
          ==> "aabb" 

Вот лучшее, что я мог придумать до сих пор:

(defmacro let-dotted-alist (alist &rest body) 
"binds the car of each element of dotted ALIST to the corresponding cdr and makes them available as variables in BODY." 
    `(let ,(nreverse 
     (mapcar  
     (lambda (p) (list (car p) (cdr p))) 
     (eval alist))) 
    ,@body)) 

Проблема здесь является eval. Мне нужно это для того, чтобы передать alist как переменную (foo), а не буквальный, что является основным прецедентом при определении функций. Для макроса для разработки кода эта переменная должна быть связана уже. Все еще везде я читал, что использование eval имеет тенденцию указывать на недостаток кода? Есть ли способ обойти это?

Это было бы несколько академической проблемой, если бы Emacs 24 не представил нетерпеливое расширение макроса, которое хочет развернуть макрос во время загрузки, когда переменная (dotlist в следующем примере), которая должна предоставить alist, по-прежнему недействительна:

(defun concat-my-cdrs (dotlist) 
    (let-dotted-alist dotlist 
      (print (concat a b)))) 

в зависимости от того, как я оцениваю это я либо получить »MAPCAR: значение символа как переменной является ничтожным: dotlist«или»Нетерпеливый отказ макрорасширение: (аннулируются переменной dotlist)«. Это имеет смысл, конечно, потому что переменный список точек действительно недействителен во время загрузки.

Теперь, прежде чем я попытаюсь найти обходное решение вдоль (локально) отключения активного макрообмена, есть ли способ улучшить определение макросов, чтобы вообще избежать eval?

Заранее благодарен!

+1

Возможно, мне не хватает чего-то тривиального ... но почему бы вам просто не использовать ', alist' вместо' (eval alist) '? (На самом деле, вы находитесь под ',' уже, так что точно отсутствует? –

+0

Извините, не обращайте внимания на мой предыдущий комментарий. Понял! –

ответ

1

Я не верю, что вы можете избежать eval, как говорится проблема: macroexpand let -обязания в зависимости от переменной. Единственным способом не использовать eval было бы отложить оценку переменной до тех пор, пока макроэкземпляр не будет, но это противоречит требованию расширения привязок let. Думаю, я заявляю то, что очевидно для вас.

Таким образом, вопрос, по-видимому, заключается в том, могут ли ваши требования быть изменены, чтобы устранить необходимость в eval. Это означало бы либо отказ от привязок let, либо создание привязок в макропараметрах. Вам решать, приемлемо ли это, но мой личный прием - eval не так уж плох, чтобы убить ваш прецедент. Я бы оставил там eval. (Я по сути тот же самый недавно в Clojure, где мне нужно было привязать одни и те же локальные символы к разным значениям, подражая функциям OCaml. Это объяснение, почему я, вероятно, склонен к тому, чтобы поддерживать то, как вы это делали.)

Я мог бы не зная о некоторых специальных настройках elisp, хотя даже тогда я, вероятно, предпочел бы eval некоторым трюкам, с которыми я до сих пор не сталкивался.

+0

Спасибо за обертывание головы вокруг этого! Я остановлю это. Мне приятно утешиться, что, по крайней мере, нет очевидной вещи, которую я пропустил. В случае, если я нахожу хороший обход для проблемы с нетерпением, я собираюсь опубликовать его здесь. – Phylax

+0

Спасибо, было бы интересно посмотреть, как вы справляетесь с этим. –

1

Во-первых, я упомянул, что eager-macroexpansion не представляет новой проблемы: эта же проблема появляется в предыдущем Emacsen, если вы пытаетесь выполнить компиляцию файла байтом.

Как для избежания eval, вы можете, используя что-то другое, которое использует eval, например.cl-progv:

(defmacro let-dotted-alist (alist &rest body) 
    (macroexp-let2 nil alist alist 
    `(cl-progv (mapcar #'car ,alist) 
       (mapcar #'cdr ,alist) 
     ,@body))) 

Но обратите внимание, что семантика немного отличается: переменные могут быть только динамической областью видимости, а не лексическую область видимости, так как список переменных, будут известны только во время выполнения.