こういうのを作りました.
このライブラリを使うと,(lambda (x y z) (foo x y z))
と書いていたところを ^(foo _ _ _)
と書けるようになります.
ちなみに名前は穴が開いたパンチカードから来ています.任意の式に穴を開けられるのでそういう名前にしました.
動機
Common Lispは関数が一級市民なので,かなりlambda式を多用します.ソートの基準やちょっとした文法の調整だとかで,いったんlambda式に焼き直して使いやすい形に変換してしまう,ということがよくあります.
しかし頻出するわりには特別なフォームといったものも特に用意されておらず,素朴に(lambda (x y) ...)
といった書き方をしていました.
ここでScalaの無名関数リテラルを見てみましょう.引数が入る位置に_
を配することで,うまく無名関数を書いています.
_ + _ // (x) => (y) => x + y
これは部分適用の構文とか,プレースホルダという名前で呼ばれています.(正式には何と呼ぶのでしょう)
ScalaでできてCommon Lispでできないのもくやしいので*1,サクッと実装してみたところ案外よく動くので公開したというわけです.
変換ルール
とりたててルールというべきものもありませんが,以下の通りに式が変換されます.
^()
これでくくった式は最終的に(lambda)
に変換されます.このカッコの中で使われているアンダースコアが引数として使われます.
_
アンダースコアです.配置した順に仮引数がつけられていきます.例えば^(* _ _ _)
としたとき,(lambda (x y z) (* x y z))
という順序で展開されます.展開するときの仮引数の名前は(gensym)
で生成しているため,内部の式と衝突することはありません.
基本的にこれが全部です.
補助ルール
これだけだと実用上(同じ変数を2度使いたいときなどに)不便なので,いくつかのルールを追加しています.
<_
引数リストの末尾にある引数を再利用します.例えば^(* _ _ <_)
は(lambda (x y) (* x y y))
と同様です.
したがって最初に使うことができません.
このプレースホルダは引数を増やしません.
必要に応じて<<_
(2つ遡る)や<<<_
(3つ遡る)も実装できますが,今のところこれででいいんじゃないかという理由で<_
だけ実装しています.
_!
通常であれば_
は書かれた順に引数リストを構成する----つまり引数リストの最後に自分自身を挿入しますが,_!
は引数リストの先頭に自分自身を挿入します.これは引数の順序を入れ替えたいときなどに便利です.まず_!
を右から順に引数リストに追加し,その後で_
を左から引数リストに追加する,という見え方になります.
(^(list _ _! _! _) 1 2 3 4) ;; => '(3 2 1 4)
これより複雑なルールが必要になったら,おとなしくlambda
と書いたほうが良さそうです.
また今後便利なルールが必要になったら,随時追加していこうと思います.こういうルールもあったほうがいいんじゃないの,といったご意見お待ちしております.
まとめ
拙作のCommon Lispライブラリcl-punch
を紹介しました.今のところquicklispには登録していませんが,手順も難しくないようなので登録してみようと思います.
*1:Scalaも大好きな言語です