はじめに
Gaucheのようなlambdaの短縮表記を実装をしようと思い,詰まったところをメモしておきます.またその際に深町さんの記事を参考にしました.また深町さんの記事内にあるコードの多くを,コピペしました.ご了承ください.
経緯
m2ymさんのプレゼン資料でCLでのlambdaの長さ(また冗長さ)について書かれているのを見て,気になるようになりました.普段コードを書くときはEmacsを使っているので,入力するときはauto-completeの補完を使用することにより特に気になることはないのですが,後でコードを見直す際lambdaが画面の多くを占めているのはやはり気になります(Emacsにはlambdaをλに変換して表示するelispはあります).
個人的にはlambdaという綴りが好きなので多くなっても問題はないですが,可読性は低くなるような気もします(関係ありませんがCLのdefunという綴りも好きです).それで色々な記事を見るとGaucheではlambdaの短縮表記を用意していることを知り,CLで実装してみようと思いました.
エラー
単純に下のように実装すると上手くいきません.
(defmacro ^ ((&rest parameter) &body body)
`(lambda ,parameter ,@body))
((^(x y) (+ x y)) 1 2)
;; Execution of a form compiled with errors.
;; Form:
;; ((^ (X Y)
;; (+ X Y))
;; 1 2)
;; Compile-time error:
;; illegal function call
;; [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
このエラーを見ておそらくリードマクロを使って読み込み時にマクロ展開をする必要があるのではないか?と推測しました.ですがまともにリードマクロを書いたことがないかつ理解もできていないので,しばらくこの疑問を放置してしまっていました.その時に深町さんの記事を見て,実装方法を知りました.
実装
リードマクロについて理解できていないので,ほぼコードをコピペするのみとなっています.また表面的にしか理解していないので誤ったコードの使い方をしている場合もあります.申し訳ありません.とりあえずコードを下に示します.
(let ((r (copy-readtable nil)))
(defun read-symbol (stream)
(let ((*readtable* r))
(read-preserving-whitespace stream))))
(defun symbol-reader-macro-reader (stream char)
(unread-char char stream)
(let* ((s (read-symbol stream))
(f (get s 'symbol-reader-macro)))
(if f (funcall f stream s) s)))
(set-macro-character #\^ 'symbol-reader-macro-reader t)
(setf (get '^ 'symbol-reader-macro)
#'(lambda (stream symbol)
(declare (ignore stream symbol))
'cl:lambda))
先程エラーが出たコードを実行してみます.
((^(x y) (+ x y)) 1 2)
;; => 3 (2 bits, #x3, #o3, #b11)
上手く実行できてます.
おわりに
Lispは奥が深いです.最近は哲学の勉強ばかりしていて更新をさぼっていましたが,また少しずつ興味がある内容について書いていこうと思います.
個人的な意見ですが哲学の勉強をすればするほど,何事にも無気力になっていくように感じました.今回勉強を再開しLispとプログラミングの楽しさを再確認し,やる気も出てきたと思います.なのでしばらくはLispとプログラミングの勉強に集中しモチベーションを高めていこうと思います.