2012-09-08

cl-fn 0.2

自分自身でもすっかり投げっぱなしで忘れていた関数ユーティリティライブラリ、cl-fnの0.2をリリースした。

  • sequentially-apply
  • ->
  • ->>

というマクロが新たに追加されている。

インストールするには、

% cd ~/quicklisp/local-projects
% wget -q -O - https://bitbucket.org/llibra/cl-fn/downloads/cl-fn-0.2.tar.bz2 | bzcat | tar xf -

みたいな感じで。普通にブラウザとかでダウンロードしてきても、最終的にQuicklispのlocal-projectsディレクトリの下でアーカイブを展開すれば大丈夫。もちろん、Mercurial使ってる人はhg cloneでもオーケー。Git使ってる人はGitHubのリポジトリがあるのでどうぞ。自分以外に使う人がいるとも思えないけど一応。

quicklisp/local-projectsの下に展開し終わったら、

> (ql:quickload :cl-fn)

すればcl-fnの関数やマクロが使えるようになる。パッケージ名はそのまんまcl-fn。

で、本題。新しく導入したマクロの話。

->->>Clojureから借りてきたもの。使い方はリンク先に載っているので読んでほしい。日本語の解説を読みたい方は「Clojureの->と->>の使い方 - あと味」辺りが詳しいのでどうぞ。簡単に説明すると、関数の戻り値を順番に適用していきたい場合に便利なマクロ。->は最初の引数、->>は最後の引数として戻り値を渡す。

sequentially-apply->->>の亜種みたいなものだけど、こちらはprogsを参考にして作った。オリジナルとの違いは、戻り値を渡す位置を「_」で指定できるようにしているのと、多値に対応していること。こんな風に使う。

(sequentially-apply (loop for n from 1 to 10 collect n)
  (reduce #'+ _)
  (print _))
;-> 55

ひとつ前の式の戻り値を使いたい場所に「_」を指定しておくと、戻り値に置き換わる感じ。

多値を使う場合はこうする。

(sequentially-apply (values 1 2 3)
  (mapcar (lambda (x) (* x 2)) (list _ _ _))
  (reduce #'+ _))
;=> 12

それぞれの値が順番に「_」の部分に展開されると考えて問題ない。ちなみに、順番の入れ替えは現状ではできないので注意。入れ替えたい場合は

(sequentially-apply (values 1 2 3)
  (let ((x _) (y _) (z _)) (list z y x)))
;=> (3 2 1)

(sequentially-apply (values 1 2 3)
  ((lambda (x y z) (list z y x)) _ _ _))
;=> (3 2 1)

のようにletlambdaを経由することになる。

ついでに、sequentially-applyについて考えたこと、考えていることとかも書いておく。

引数を「_」で指定することについては、タイプ数が少し増えるけど、->->>みたいに複数のマクロを作る必要がないこと、それぞれの式が本来の呼び出しの形に似ていることからこうしてある。その辺りが好みじゃなければ->->>を使ってくださいというスタンス。

多値の順番の入れ替えについては、「_1」「_2」みたいに順番を指定する、みたいなことは割と簡単にできるけど、どうなんだろう。自分的にはそんなにない状況な気がするし、letで良いんじゃ、とか思うけど、良くわからない。不便だと思ったらいつか対応するかも。ただ、「_1」と「_」が混ざったらどうするの、とか考えると割と微妙かもしれない。

名前については、良い感じの短い名前が思い付かなかった。個人的に記号だけの識別子は好きじゃないから避けたいけど、pipeとかも微妙に感じるし……。将来的に変えるかもしれない。記号が駄目なら->とかはどうなんだ、という話だけど、そっちも良い名前が思い付いたら変えるかも。ただ、元の->とかも別名として残すとは思う。逆にsequentially-applyの方も、長いから「>>」っていう別名付けてよ、みたいに誰か他の人が言ってきたら多分対応する。まあ、まずあり得ないからこっちは気にしない。

こんなところかな。lambdaの代わりのfn(「_」で引数無視できて楽)とか、定番のcurryrcurrycomposeconjoindisjoinflipとか、(setf (symbol-function ...) ...)の代わりのdefaliasとか、あとは今回の->->>sequentially-applyとか、そういうのを求めてる人は気が向いたらどうぞ。

2 件のコメント:

ichimal さんのコメント...

cl-monad-macrosのlet!も同系統といえば同系統ですかね。
さすがにあれは大仰しいという気もしますが。

llibra さんのコメント...

言われてみれば、let!とかのletMの系譜も、適用の連鎖という意味では似たようなものですね。

ちなみに、実際のコードで色々使ってみた結果、構文糖としてのsequentially-applyはLispでは微妙、という割とガッカリなポジションを確保しました。(元から括弧が多いので、let*で結果を一時変数に保存して処理する場合と見た目に大差ない。途中に多値が混じると割と効果的ですが、let+で似たようなことができるはずなので、こちらも微妙)