Haskellの有名な特徴として、関数が勝手にカリー化されるという点があります。
要するにHaskellの関数は常に部分適用可能になっていて、f foo bar baz
という関数適用は(((f foo) bar) baz)
と解釈されています。これは非常に強力な機能で、汎用的な関数を目的に合わせて簡単に特殊化することができます。
Schemeやその他のLispでは、引数は必ず同時に与えないといけないので、カリー化したものを作ろうとするとクロージャを使って(define f (lambda (x) (lambda (y) (lambda (z) ...))))
とでもしなければなりません。しかも呼び出すときには(((f foo) bar) baz)
と、1つずつ順番に適用する必要があります。
私が欲しいのは、"(Haskellが透過的にやっているように)与えられた引数を先頭から順に束縛し、足りない分を引数とするクロージャを返す"ような関数です。((f foo) bar baz)
だろうが(f foo bar baz)
だろうが(((f foo) bar) baz)
だろうが同じ結果を返す関数を作りたいわけです。そこで、カリー化関数を作成するマクロを書きました。
(define-syntax curry
(syntax-rules ()
((_ (arg0) body ...) (lambda (arg0) body ...))
((_ (arg0 arg1 ...) body ...)
(lambda (arg0 . rest)
(define applied (curry (arg1 ...) body ...))
(if (null? rest) applied
(apply applied rest))))))
このマクロを使って作成した関数は、引数を先頭から束縛していき、すべての引数が揃ったときに値を返します。
(define greet (curry (when who)
(display
(string-append "good "
when ", " who "."
" how are you?"))
(newline)))
(greet "morning" "John") ;;; prints "good morning, John. how are you?"
((greet "morning") "John") ;;; same as above
バグなどありましたらコメントで教えてください。
コメント
コメントを投稿