В Emacs 24 добавлены необязательные лексические привязки для локальных переменных. Я хотел бы использовать эту функциональность в своем модуле, поддерживая совместимость с XEmacs и предыдущими версиями Emacs.
До Emacs 24 самым простым способом получить замыкания была форма lexical-let
, определенная в cl-macs
, которая эмулировала лексическую область с помощью некоторых умных макросов. Хотя это никогда не было чрезвычайно популярным среди программистов elisp, оно действительно работало, создавая реальные и эффективные закрытия, если вы запомнили их обернуть в lexical-let
, как в этом псевдокоде:
(defun foo-open-tag (tag data)
"Maybe open TAG and return a function that closes it."
... do the work here, initializing state ...
;; return a closure that explicitly captures internal state
(lexical-let ((var1 var1) (var2 var2) ...)
(lambda ()
... use the captured vars without exposing them to the caller ...
)))
Возникает вопрос: какой лучший способ использовать новые лексические привязки, сохраняя поддержку Emacs 23 и XEmacs? В настоящее время я решил это путем определения макроса, специфичного для пакета, который расширяется до lexical-let
или в обычном let
в зависимости от того, привязан ли lexical-binding
и true:
(defmacro foo-lexlet (&rest letforms)
(if (and (boundp 'lexical-binding)
lexical-binding)
`(let ,@letforms)
`(lexical-let ,@letforms)))
(put 'foo-lexlet 'lisp-indent-function 1)
... at the end of file, turn on lexical binding if available:
;; Local Variables:
;; lexical-binding: t
;; End:
Это решение работает, но оно неуклюже, потому что новая специальная форма нестандартная, не выделяется должным образом, не может быть переделана под edebug
и вообще привлекает внимание к себе. Есть ли лучший способ?
ИЗМЕНИТЬ
Два примера идей для более умных (не обязательно хороших) решений, которые позволяют коду продолжать использовать стандартные формы для создания замыканий:
-
Используйте совет или макрос компилятора, чтобы сделать
lexical-let
expand доlet
подlexical-bindings
, еслиlexical-let
назначает только символы, которые лексически ограничены. Этот совет будет только временно активироваться во время байтовой компиляцииfoo.el
, так что значениеlexical-let
остается неизменным для остальных Emacs. -
Используйте средство макрокоманды/кода для компиляции
let
символов без префикса доlexical-let
под старым Emacsen. Это снова применимо только во время байтовой компиляцииfoo.el
.
Не волнуйтесь, если эти идеи пахнут чрезмерной инженерией: я не предлагаю использовать их как есть. Меня интересуют альтернативы указанному выше макросу, где пакет получает выгоду от более удобного переносного использования закрытий по цене некоторой дополнительной сложности загрузки/компиляции.
РЕДАКТИРОВАТЬ 2
Поскольку никто не активировал решение, которое позволило бы модулю продолжать использовать let
или lexical-let
, не нарушая их для остальных Emacs, я принимаю ответ Stefan, в котором говорится, что указанный макрос является способ сделать это. В дополнение к этому ответ улучшает мой код, используя bound-and-true-p
и добавляя элегантное объявление для edebug и lisp -indent.
Если у кого-то есть альтернативное предложение для этого уровня совместимости или элегантная реализация вышеупомянутых идей, я призываю их ответить.