Code Aquarium

minazoko's blog -*- 水底のブログ -*-

関数の引数をlexical-letするマクロ

続編を書きましたのでそちらもよろしく。

Emacs Lispを書いていると関数の引数が動的スコープで困ることがある。で、書いてみたマクロ

(require 'cl)

(defmacro* defun% (name args &rest rest)
  (let* ((docstr (when (and rest (stringp (car rest)))
                   (car rest)))
         (body (if docstr (cdr rest) rest))
         (pred (lambda (x)
                 (string-prefix-p "&" (symbol-name x))))
         (args- (remove-if pred args))
         (args- (mapcar (lambda (a) `(,a ,a)) args-)))
    (append `(defun ,name ,args)
            (when docstr `(,docstr))
            `((lexical-let ,args- ,@body)))))

たとえば

(defun% foo (x y z)
  (+ x y z))

と書くと

(defun foo (x y z)
  (lexical-let ((x x) (y y) (z z))
    (+ x y z)))

という風に展開してくれる。

制限事項

  • &optional はデフォルト値指定不可(elispだとそもそも無理?)
  • &key はサポートしてない。

追記(2012/12/11)

emacs lispのdefunは、&keyも&auxもサポートしていませんでした。&restと&optionalがサポートされていますが、&optionalはデフォルト値は指定できません。これらを使いたいときはclモジュールで定義されているマクロdefun*を使いましょう。ただしdefun*の引数もdefun同様に動的スコープみたいです。