スキップしてメイン コンテンツに移動

Schemeでカリー化

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

バグなどありましたらコメントで教えてください。

コメント

このブログの人気の投稿

部分継続チュートリアル

この文書についてこれはCommunity Scheme Wikiで公開されているcomposable-continuations-tutorial(2010年09月30日版)の日本語訳です。誤字脱字・誤訳などがありましたらコメントあるいはメールで御指摘いただけると幸いです。本訳は原文のライセンスに基づきCreative Commons Attribution-ShareAlike 2.0 Genericの下で公開されます。Original text: Copyright© 2006-2010 Community Scheme WikiJapanese translation: Copyright© 2011 SATOH Koichi本文部分継続(Composable continuation)は継続区間を具象化することで制御を逆転させるものです。 ウンザリするほど複雑な概念を表す長ったらしいジャーゴンのように聞こえますが、実際はそうではありません。今からそれを説明します。resetとshiftという2つのスペシャルフォームを導入するところから始めましょう[1]。 (reset expression)は特別な継続を作るなりスタックに目印を付けるなりしてからexpressionを評価します。簡単に言えば、expressionが評価されるとき、あとから参照できる評価中の情報が存在するということです。 実際にはshiftがこの情報を参照します。(shift variable expression)は目印のついた場所、つまりresetを使った場所にジャンプし、その場所からshiftを呼び出した場所までのプログラムの断片を保存します; これはプログラムの区間を「部分継続」として知られる組み合わせ可能な手続きに具象化し、この手続きにvariableを束縛してからexpressionを評価します。組み合わせ可能(Composable)という語はその手続きが呼び出し元に戻ってくるため、他の手続きと組み合わせられることから来ています。 Composable continuationの別名として例えば限定継続(Delimited continuation)や部分継続(Partial continuation)もありますが、ここでは一貫して「組み合わせ可能」という用語を使います(訳注: …

Perl 5 to 6 - 列挙型

これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 16 - Enumsの日本語訳です。原文はCreative Commons Attribution 3.0 Germanyに基づいて公開されています。本エントリにはCreative Commons Attribution 3.0 Unportedを適用します。Original text: Copyright© 2008-2010 Moritz LenzJapanese translation: Copyright© 2011 SATOH KoichiNAME"Perl 5 to 6" Lesson 16 - 列挙型SYNOPSISenum bit Bool <False True>; my $value = $arbitrary_value but True; if $value { say "Yes, it's true"; # 表示される } enum Day ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'); if custom_get_date().Day == Day::Sat | Day::Sun { say "Weekend"; } DESCRIPTION列挙型は用途の広い獣です。定数の列挙からなる低レベルのクラスであり、定数は典型的には整数や文字列です(が任意のものが使えます)。これらの定数は派生型やメソッド、あるいは通常の値のようにふるまいます。 but演算子でオブジェクトに結びつけることができ、これによって列挙型を値に「ミックスイン」できます:my $x = $today but Day::Tue; 列挙型の型名を関数のように使うこともでき、引数として値を指定できます:$x = $today but Day($weekday); こうするとオブジェクトは列挙型の名前(ここではDay)をメソッド名として持ちます:say $x.D…

去る6月に Perl 5.32.0 がリリースされたので差分を把握するために perldelta を読んだ件

要旨Perl 5 メジャーバージョンアップの季節がやって来たのでまともな Perl プログラマの嗜みとして perldelta を読んだ。今回は有り体に言えばルーティン的なリリースで、言語コアの拡張は他言語にも見られる構文が実験的に入ったくらいで大きな変化はない。新機能は RegExp の拡充が主である。比較的重要と思われる変更点を抜粋する。新機能isa 演算子実験的機能。Python とか Java における isinstance とか instanceof。これまでも UNIVERSAL::isa があったが、これはメソッドなのでレシーバにオブジェクトでもクラスでもない値 (i.e., 未定義値 / bless されていないリファレンス) を置くと実行時エラーが起きるのが問題だった:package Foo {use Moo;}package Bar {use Moo; extends 'Foo';}package Baz {use Moo;}use feature qw/say/;sub do_something_with_foo_or_return_undef {my ($foo) = @_;# Returns safely if the argument isn't an expected instance, in mind.returnunless$foo->isa('Foo'); ...;}# OK.do_something_with_foo(Bar->new);# |undef| is expected in mind, but actually error will be thrown.do_something_with_foo(undef); これを避けるために今までは Scalar::Util::blessed を併用したりしていたわけだが、isa 演算子は左辺が何であっても意味のある値を返すのでよりシンプルになる:# True+(bless +{} => 'Foo') isa Foo;# Falseundef isa Foo;# False+{} isa Foo;# False+(bless +{} => 'Baz') isa Foo; 比較演算子…