cons
を例に挙げると、
この手続きから一項演算である (lambda (x) (cons 1 x))
を生成したいことがあるかもしれない。
このようなパラメータの特殊化は、
「部分適用」(partial application)、「演算子切断」(operator section)、「射影」(projection)
などと呼ばれることもある。
この SRFI で提案する仕組みを使えば、 このような特殊化を簡単に記述することができるようになる。 この仕組みを理解するために、いくつかの例を挙げよう。
(cut cons (+ a 1) <>)
| は右の式と同じ | (lambda (x2) (cons (+ a 1) x2))
|
(cut list 1 <> 3 <> 5)
| は右の式と同じ | (lambda (x2 x4) (list 1 x2 3 x4 5))
|
(cut list)
| は右の式と同じ | (lambda () (list))
|
(cut list 1 <> 3 <...>)
| は右の式と同じ | (lambda (x2 . xs) (apply list 1 x2 3 xs))
|
(cut <> a b)
| は右の式と同じ | (lambda (f) (f a b))
|
上記のように、 cut
マクロは第一引数に指定された手続きのパラメータを特殊化する。
結果として生成される手続きの仮引数となるのは、
<>
というシンボル (「スロット」と読む) で表される。
また、<...>
というシンボル (「残余スロット」と読む)
は、手続きの残余引数を渡すことを表す。
最後の例を見ると分かるように、
第一引数にスロットを指定することもできる。
cut
の他に、その変種である
cute
というマクロもある
(この名前は "cut
with evaluated non-slots" に因む)。
このマクロは、手続きが呼び出される時点ではなく、手続きが特殊化される時点で、スロット以外の式を評価する。
例を挙げよう。
(cute cons (+ a 1) <>)
| は右の式と同じ | (let ((a1 (+ a 1))) (lambda (x2) (cons a1 x2)))
|
この例と前述の最初の例とを比べると分かるように、
cute
では (+ a 1)
が1度だけ評価されるのに対し、
cut
では生成される手続きが呼び出されるたびに評価される。
この SRFI で提案する仕組みは、手続きの引数の任意のサブセットを特殊化することができる。
結果として生成される手続きは、固定個引数にも可変個引数にもできる。
しかしこの仕組みは、引数の順序を入れ替えたり、省略したり、重複させたり、
あるいは、引数を処理したりはできない。
そのような必要があれば、lambda
のような別の仕組みを使わなければならない。
(cons 1)
と書くことができる。
しかし、Scheme はカリー化が可能な言語ではない。
手続きに渡す引数の個数は、仮引数の個数と常に一致しなければならない。
これにより、引数なしの手続きや、可変個引数の手続きを使うことはできるが、
引数を特殊化するためには、lambda-式を書き、
仮引数を指定するための不適切な識別子を発明しなければならない
(上述の例の x
がそれである)。
このような理由から、
この SRFI では、
手続きの引数の任意のサブセットを特殊化するための
シンプルでコンパクトな記法を提案する。
ノート: ここで提案している仕組み自体はカリー化ではない!
ここで提案する仕組みの目的は、 Scheme プログラミング言語において、 カリー化の恩恵を享受できるようにすることである。 実際面において、カリー化には主に2つの利点がある。 高階型を単純化することができるということと、 引数を特殊化するための記法が簡単になるということである。 Scheme は動的型付け言語なので、型に対する利点はないに等しい。 この SRFI では、特殊化に関して言及する。
説明のために、もう少し例を挙げよう。
(map (cut * 2 <>) '(1 2 3 4))
|
(map (cut vector-set! x <> 0) indices)
|
(for-each (cut write <> port) exprs)
|
(map (cut <> x y z) (list min max))
|
(for-each (cut <>) thunks)
|
<cut-expression>
| -->
| (cut <slot-or-expr> <slot-or-expr>*)
| ||
| | (cut <slot-or-expr> <slot-or-expr>* <...>)
| ; 残余スロットを伴う場合 | ||
| | (cute <slot-or-expr> <slot-or-expr>*)
| ; 特殊化時に非スロットを評価する | ||
| | (cute <slot-or-expr> <slot-or-expr>* <...>)
| ; 残余スロットを伴う場合 | ||
<slot-or-expr>
| -->
| <> | ; スロット | |
| | <expression> | ; 非スロット式 |
cut
マクロは
<cut-expression>
を
<lambda expression>
に変換する。
この lambda-式は
<slot-or-expr>*
リストの中のスロットの個数だけ仮引数をとる。
結果として生成される
<lambda expression>
の本体は、最初の
<slot-or-expr>
を呼び出し、
<slot-or-expr>*
を引数として渡す。
引数の順序はマクロに指定された順序そのままである。
残余スロットが存在する場合、
生成される手続きも可変個引数をとる。
その本体は最初の
<slot-or-expr>
を呼び出し、特殊化された手続きに引数を渡す。
cute
マクロは cut
マクロと同様だが、
最初に非スロット式を評価して (評価の順序は不定) その値を新しい変数に束縛し、
非スロット式をその変数に置換する。
要するに、
cut
は、生成された手続きを呼び出した時点で
非スロット式を評価するが、
cute
は、手続きを生成する時点で
非スロット式を評価する。
cut
と cute
の2つのマクロを定義する。
他の SRFI やライブラリは一切使用していない。
この実装では
<slot-or-expr;>
リストを列挙するために、内部的なマクロを使用している。
R5RS のマクロはハイジニックで参照透過であるため、
このマクロで新しく導入される仮引数の名前は一意であり、
名前の衝突が起きることはない。
テンプレート (param ... slot)
(
R5RS 7.1.5 節
を参照)
は、引数の順序を保つ。
この参照実装は Al Petrofsky により実装された。
ここから入手できる。
最後に、小さな信頼性テストを用意した。 この SRFI で定義された仕組みに対して、 いくつかの特殊ケースを検証し、 何か間違いがあればエラーを出すようにしている。 ただし、このテストに合格するからと言って、 正しい実装であるとは限らない。
cut
マクロの目的である。
Scheme においてカリー化/非カリー化を持ち出すことが妥当なのは、
主にコンビネータ論理 (combinatory logic)
の概念を教える場合である。
cut/cute
なのですか?curry
だったが、よく知られているカリー化とは別物だという事実に、
すぐに具合いの悪さを感じ始めた。
他の案も提案された。たとえば、
section
, specialise
,
specialize
, partial-apply
,
partial-call
, partial-lambda
,
_j
, _i
, $
,
&
, srfi-26
, foobar
,
xyz
, schoenfinkelize
,
curry-which-isnt-curry
, tandoori
などである。また、ランダムに5文字を選んでそれを名前にするという提案もなされた。
とは言うものの、これらの名前がすべてまじめに提案されたわけではない。
これらのうちのいくつかは、単に論点を説明するためのものだ。
さらに、私はこれらの名前と戯れながらかなりの時間を過ごし、
ここに載せてはいない他の候補についても考えあぐねた。
議論メーリングリストに出てくる意見は、
世の人々の意見に対するかなり偏った無作為標本ではあるが、
違った名前を提案してくれることは
この SRFI にとって有益なことであると思った。
しかし、満場一致の同意を得るのは現実的ではないだろう。
cut
という名前は「演算子切断」(operator section)
を参考にしている。他のプログラミング言語では、この概念がよく使われるからである。
しかし、私はどちらかと言えば "Curry Upon This" の頭文字語として覚えている ;-)。
cut
の評価バージョンの名前としては
cut!
, cutlet
,
cut*
, cute
が提案された。
cute
の仕組みを手続きとして実装することは可能だ。
詳細については Al Petrofsky の投稿
"Problems with "curry"'s formal specification"
も参照せよ。
しかし、手続きによる実装では、少し性能が落ちることになる。
それから、cut
の仕組みを手続きとして実装することは不可能である。
両方の仕組みが必要であるため、この SRFI ではマクロによって実装している。
cut
は、特殊化された手続きが呼び出されるときに、すべての非スロット式を評価する。
cute
は、手続きが特殊化されるときに、すべての非スロット式を評価する。
これらは両極端であり、非スロット式ごとに評価のタイミングを選択できる構文を定義することは可能だ。
しかし、そのような柔軟性を与えても、混乱が起きるだけで益がないと思う。
もし、そのような細かい指定が必要になるなら、
let
と lambda
を使って明示的に手続きを構築するほうがよいだろう。
(cut if <> 0 1)
のような式は正しくないのですか?<slot-or-expr>
はスロット シンボルであるか、
あるいは、
R5RS,
Section 7.1.3
の意味の <expression>
でなければならないという仕様である。
if
は <expression>
ではないので、
上記の式は間違っている。
cut
と cute
がこのように制限されている理由は、
そのような一般化された式の意味を定義することが困難だからである。
詳細については議論のアーカイブを参照してください。
Copyright (C) Sebastian Egner (2002). All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.