名前付き引数

Queenの文法紹介(に見せかけた何かへの言及)第二回は名前付き引数です。
名前付き引数への反応といったら大抵拒否反応で、かなり前にも別の話の中でしたがこんなコメントをいただいたことがあります。
http://d.hatena.ne.jp/ytqwerty/20040626#p1

だもんでおそらくVBスタイルで引数を指定する機構が必要になるかと思うんですけど、あれはあれですごいウザー(゜Д゜)

名前付き引数が後付けされているような言語だと、まあ、同意です。元々BASICは、CONSOLE,,,,1みたいな過激な省略の仕方をする言語ですしね。*1
DelphiでもOleVariant限定で同様の構文を使えますが、やはりウザーです。
では、後付けではない、名前付き引数が設計当初からある言語ではどうでしょう?
…とかいってこれがまたAdaなんですから私も病気だなあ…しみじみ。
ええと、Adaでの名前付き引数の使用は、めっさ快適です。
主な用途としてはやはり同じ型の引数が複数ある場合に適切なコンパイルエラーを貰うため、になるのですが、関数というよりはgenericですね。C++でも、順番間違えてわけわからんことになる度合は、構造体や関数よりもtemplateが上でしょう。書き換え時に追従しなければならない箇所がコンパイルエラーで一覧になるのはとても便利です。いちいち使われている箇所をgrepして回れますかっての。
あと大きいのは、コンストラクタの引数といいますか、いやAdaにC++風のコンストラクタは無いのですが、型にシングルクオーテーション付けて括弧を続けることで、任意のレコードや配列の値をインラインに書くことができます。便利です。*2同様の機能が(こっちは後付けでしかも使われているところを見かけたことはありませんが)C99にも。

return My_Unconstrained_Array'(1, 2, 3);

で、Pascal系言語の配列は下限もフリーで、Adaの無制約配列は初期化時に範囲を定めることができますから、こんな風に書けます。

return My_Unconstrained_Array'(4 => 1, 5 => 2, 6 => 3);

また、subtype My_Constrained_Array is My_Unconstrained_Array(1 .. 10); なんてのがあったりすると、こっちは1から10まで埋めないといけないのですが、5番目の要素だけ10にして後は0でいい時はこう書きます。

return My_Constrained_Array'(5 => 10, others => 0);

C関数に渡そうとしている文字配列で、先頭だけ0入れてあとは未初期化でよい場合はsubtype C_chars_256 is Interfaces.C.chars(0 .. 255);としてこんな風にも書けます。

return C_chars_256'(C_chars_256'First => Nul, others => <>);

さて、最初の例ですが、Adaは返値でもオーバーロードができます。逆に言えばその式が要求されている型をコンパイラは認識しています。ので、結構過激な省略ができます。

return (1, 2, 3);
return (4 => 1, 5 => 2, 6 => 3);
return (5 => 10, others => 0);
return (C_chars_256'First => Nul, others => <>);

思いっきり「逆方向の推論」(モドキ)ですが、0やnullを要求されている各ポインタ(or 参照 or アクセス)型にしてしまうことぐらいはどんな言語でもやってますぜ、DelphiD言語は要求に合わせて文字列のエンコードすらしますぜ、と横道にそれた上で。
ここで、先頭のケースで、要素数を減らしていきましょう。

return (1, 2);

配列なのかrecordかはともかく、まだ何かのaggregateということはわかります。

return (1);

ここまでくると、括弧が無駄に書かれているのと区別ができません。*3
では要素数1の構造化型の値をインラインに書くことはできないかというとそんなことはなくて、ここで名前付き引数が生きてきます。

return (1 => 1);

型はもちろんMy_Unconstrained_Array(1 .. 1)。
他にも、区別不可能なオーバーロードを解決したりなど、小技色々。
さて、話は飛んで、CecilやIo、これらの言語では、if文も、単なる関数呼び出しです。CecilやIoは関数呼び出しに括弧が必要なため、違和感が残るifです。
では、関数呼び出しに括弧が要らないHaskellHaskellのifは関数ではなく構文です。

min x y = if x < y then x else y

ifをラップしてみましょう。

myif c t e = if c then t else e
min x y = myif (x < y) x y

これをよーく見てください。
予約語のthenとelseが名前付き引数に見えてきませんか?
…え?見えない?
見えてきませんか?
見えてきませんか?
見えますよね、ね。
見えましたね。
これであなたも私の仲間です。
Queenでもifは予約語ではなく通常の関数と同じように呼び出します。ただしデフォルト引数は無いことになってますのでelseが省略できるifはスペシャルフォームではあります。
Queenの関数呼び出しはHaskellにカンマ*4ですので、ifはこうなります。

if (x < y), do ( result := x ), do ( result := y );

なんでresult変数を持ち出してきたかというと、式中でのifの使用をまだ実装してないから…なんてのはどうでもいいですね。
これもよーく見てください。
thenとelseを書きたくなってきませんか?
特に、elseは、順番を間違うことなんて無きに等しいと言っても過言ではないにも関わらず、無いと、寂しかったり落ち着かなかったりしませんか?
書きたくなってきませんか?
書きたくなってきませんか?
書きたいですよね、ね。
書きたいのですか、それはよかった。
これであなたも私の仲間です。
名前付き引数を使ってthenとelseを書きましょう。

if (x < y), then: do ( result := x ), else: do ( result := y )

めでたしめでたし。
ついでにQueenではインデントが通常の括弧と同じですので最終的な形はこうなります。

if (x < y)
, then: do
    result := x
, else: do
    result := y

あるいは

if (x < y), then: do
    result := x
, else: do
    result := y

本当はifの後にもcondition:がありますが、こんなのは書きたくならないので書きません。またどちらかといえばthen, elseよりもdoのほうがウザーですが、この式中のdoによってdownward closureを認識してますので省くわけにもいかず。HaskellやIoのように遅延評価を行うなら不要なのですが、downward closure方式にも利点はあって、同じものを複数箇所で引数として使うことができます。目立たない記号に変えることは検討中。
まだ実装してませんがcase文のwhenなども同様に書けるようにするつもりですし(同じ名前の引数を複数列挙することになりますのでやはりスペシャルフォーム)、Queenでは単なるbreakにも用いるので正常フローでもある例外処理ハンドラなんかも、似通った構文にしてあるつもりです。
ちなみに発想の元凶?はこの文章だったり。
http://www.kmonos.net/alang/etc/cecil.php

{...} で Closure を作ったりif/whileが(予約語でもSpecial Formでもなく) 関数だったりする言語は個人的に非常に好きなので好感度120%アップなのですが、 しかしこの構文でそれをやるとif-then-elseがやたら読みにくい感じが。 あまり使わないのだろうか?

あとは、昔々Mindあたりを弄ったときに、日本語プログラミング言語なら助詞でオーバーロードできなきゃダメだろとか思ったのを引きずってるらしい。

*1:VBはBASICと認めてなかったんじゃなかったのか私、とひとりツッコミ。

*2:でもコンストラクタっぽい実体が生成されちゃいます。どうせ全部インライン化されてるのにいらねえ、どうしてくれよう。やっぱldにはスマートリンク機能が必要ですってば。参照されて無い関数を実行時に拾えなくなるからなんだっていうんです。(←誰もそんなことは言ってない筈)

*3:つまりパーサは単純なパースしかしてないわけです。また、これもC++0xC#,Dのautoが推論では無いのと同様に推論では無いです。(←こだわるなぁ私…)

*4:Haskellがカンマ不要なのは関数呼出しが左結合でa b c dが( ( (a b) c) d)でカリー化しつつ順次呼んでいるとも解釈できるようにするためと思うのですけれど、Haskellでも$で右結合として使うことをよくするみたいですしnotや"-"の優先順位も考えると右結合の方が便利っぽいので短絡的に(GC無しのネイティブコンパイラでカリー化は骨ですのですっぱり諦めて)右結合でa b c dが(a (b (c d) ) )、加えて複数の引数を結合強度に逆らって渡せるようにカンマが必要、と。