Posted 2016-11-09 00:47:51 GMT

前々からCommon Lispの with-open-file はいまいちな使い勝手と思っていました。

例を挙げると、

入力は楽だけど、出力の書式が面倒 open を下請けに使っているので、引数オプションは open と同じだが、開発環境の補完機能では大抵表示されない(やればできますが)

etc.. あたりです。

これらの使い勝手を改善する目的で、俺ユーティリティには、 with-output-from-file 等の定義があったりするのが定番です。

代表的なところでは、 alexandria:with-input-from-file 、 alexandria:with-output-to-file あたりでしょうか。

しかし、これらも上記に挙げた欠点は部分的にしか解決していないことが殆どです。

明示的にストリームを開く関数を書くスタイル

そんな日々でしたが、Multics MACLISP のマニュアルを眺めていたところ、 openi 、 openo 、 opena という関数を見付けました。

Common Lispで定義すると、

( defun openi ( file &key ( element-type 'cl:character ) ( external-format :utf-8 ) ) ( open file :direction :input :element-type element-type :external-format external-format ) ) (defun openo (file &key (element-type 'cl:character) (external-format :utf-8)) (open file :direction :output :element-type element-type :external-format external-format :if-exists :supersede :if-does-not-exist :create)) (defun opena (file &key (element-type 'cl:character) (external-format :utf-8)) (open file :direction :output :element-type element-type :external-format external-format :if-exists :append :if-does-not-exist :create))

位の所ですが、Inputのi、Outputのo、Appendのa がopenの後ろに一文字付いています。

使い方としては、

( with-open-stream ( out ( opena "/tmp/foo" ) ) ( write-line "おはよう日本" out ) ) (with-open-stream (in (openi "/tmp/foo")) (read-line in)) ⊳ "おはよう日本"

こんな感じで with-open-file と大差ないのですが、出力の方の書式はすっきりしています。

with-open-stream というか明示的にストリームを開く関数を書くスタイルの利点ですが、

ストリームを開く関数を予め用意しておけばすっきり記述できる 開発環境の補完機能が活用できる ストリームを開く関数をさらにラップして活用しやすい

位が挙げられるでしょうか。

「ストリームを開く関数をさらにラップして活用しやすい」というのは、テストでの差し替え等が一例として挙げられるかと思います。

( defun latumofis ( ) ( let ( ( dat ( with-open-stream ( in ( open-foo ) ) ( read-line in ) ) ) ) ( make-list 10 :initial-element dat ) ) ) (defun open-foo () (openi "/tmp/foo")) (latumofis) ⊳ ("おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本" "おはよう日本") (lispworks:defadvice (open-foo test :around) () (make-string-input-stream "おやすみ日本")) (defun open-foo () (make-string-input-stream "おやすみ日本")) (latumofis) ⊳ ("おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本" "おやすみ日本") (lispworks:remove-advice 'open-foo 'test)

まとめ

with-open-file というマクロは柔軟性が乏しく、これを土台に発展させようとしても、元の欠点を解消できない alexandria:with-output-to-file のような派生を生んでしまうという点で、マクロの例としてはあまり良くないものの代表という気がします。

こういう進化の袋小路のようなマクロは結構ありがちですが、マクロの設計もただ式を列挙するだけでなく、できるだけコンポーザブルな構成が可能になるように設計した方が良さそうです。

■

