名前の諸相
名前付き引数の是非
named arguments considered harmful - ものがたり
名前付きパラメータと位置によるパラメータの一致の不一致 - L'eclat des jours(2008-05-27)結論的には今はいらない。今いらないのはVBがマルチパラダイム言語になって、普通のオブジェクト指向言語的な書き方や関数のオーバーロードが書けるようになったから。
しかし、関数のオーバーロードがなかったVB6まではそれがだめな方法であったにしろ名前付き引数は必要悪だったと思う。
また、VB.VBAのあのIDEがあったからこそ成り立っていたとも言えるかもしれない。
VBに限らず、歴史のある言語の言語仕様について、今の視点で単純に話してしまうといささか誤解が生じるかもしれない。なんというか、VBxに関するあれこれを読んでいると一度VB.NETで断ち切ろうとしたもろもろを結局断ち切れないのだなぁという、ある種残念な気分になる。それだけ普及して、プログラマに染み付いてしまったのであれば、意識改革じゃ済まないかもしれないな。
歴史のせいで偶然と必然の区別が本当に難しいってのはまあ同意.ただ,名前付き引数が本当に(必要)悪か,関数のオーバーロードが(相対的に)良いものかどうかはよく分からない.特定言語(での実装)に強く依存して概念の善し悪しをラベリングしていないか,そこが正直怖い.
例えば HTTP Get での URL なんかはまさに名前付き引数っぽく見える.もしも http://www.google.co.jp/search に渡すクエリパラメータの「名前」を変えようものなら,地球は大地殻変動に襲われ,地軸はねじ曲がり,5つの大陸はことごとく引き裂かれ,海に沈んでしまうかもしれない.恐ろしい世の中だ.
F# の名前付き引数
「named arguments considered harmful - ものがたり」の コメント欄 にて id:kmizushima さんが OCaml のラベル付き引数を例に挙げられているけど,F# ではその辺の特徴がごっそりカットされていて,むしろ Visual Basic に近くなっているように見える.その一方で,Visual Basic と F# の間には,引数省略時の振る舞いを C++ のように「デフォルト値」で補うか,option 型という総称概念に対応付けるかの違いも見て取れる.このあたりは OCaml の血だろうか.
#light type Vector(dx:float, dy:float, ?dz:float, ?dw:float) = member this.DX = dx member this.DY = dy member this.DZ = defaultArg dz 0.0 member this.DW = defaultArg dw 1.0 member this.Length = sqrt(dx*dx + dy*dy + this.DZ*this.DZ + this.DW*this.DW) Vector(1.0, 2.0) Vector(1.0, 2.0, 3.0) Vector(1.0, dy=2.0) Vector(1.0, dy=2.0, dz=3.0) Vector(1.0, 2.0, dw=4.0) Vector(dx=1.0, dy=2.0) Vector(dy=1.0, dx=2.0) Vector(dy=1.0, dz=3.0, dx=2.0)
C# のオブジェクトイニシャライザとその記法的類似性
最近の C# で「名前」といえば,C# 3.0 の Anonymous Type と Object Initializer が印象深い.特に Object Initializer についてはデブサミで波村さんも強調されていた.
次に、Productクラスのコンストラクタを見てください。
public Product(int productID, int categoryID, string productName, decimal unitPrice){ ... }当たり前のことですが、引数の順番は重要で、引数が意味を持ち、その順番を間違えると実行時のエラーつまりBugの原因になります。例えば、
new Product(2, 1, "Chang", 19.0000M)と書くところを productID と catetoryID を間違って
new Product(1, 2, "Chang", 19.0000M)としたとします。
このコードはSyntax的には正しいですのでコンパイルも問題なく通り、実行することができます。しかし、実行時の結果は予期しなかったものになります。C# 3.0のオブジェクト イニシャライザ構文を使っていただくことにより、もちろん同様の間違えをすることは可能ですが、どのメンバを初期化しているのかを明示できる、よってエラーの原因を減らすことにつながります。
面白いことに,これを並べるとどこかで見たようなコードに見えてくる.
var productList = new List<Product> { new Product { ProductID = 1, CategoryID = 1, ProductName = "Chai" , UnitPrice=18.0000M }, new Product { ProductID = 2, CategoryID = 1, ProductName = "Chang" , UnitPrice=19.0000M }, new Product { ProductID = 3, CategoryID = 2, ProductName = "Aniseed Syrup" , UnitPrice=10.0000M }, new Product { ProductID = 4, CategoryID = 2, ProductName = "Chef Anton's Cajun Seasoning" , UnitPrice=22.0000M }, new Product { ProductID = 5, CategoryID = 2, ProductName = "Chef Anton's Gumbo Mix" , UnitPrice=21.3500M }, new Product { ProductID = 6, CategoryID = 2, ProductName = "Grandma's Boysenberry Spread" , UnitPrice=25.0000M }, (以下略)
つまり,以下のような話は C# でも起こりうる.
ただ、スクリプト言語屋がみな柔軟な頭をしてるのかとか、本質的でない記述を徹底的に排除する姿勢を持っているのかといえば、みんながみんなそういうわけでもない。例えば、スクリプト言語で次のような Hash や Dict を書いたとする。
data = [ {'name'=>'Foo', 'age'=>20, 'email'=>'foo@mail.com'}, {'name'=>'Bar', 'age'=>21, 'email'=>'bar@mail.net'}, {'name'=>'Baz', 'age'=>22, 'email'=>'baz@mail.org'}, ]もしこれをみて何も感じないのであれば、スクリプト言語屋といえど
Java屋Java信者を笑うことはできない。本質的でない記述に嫌悪感を感じるセンスがあれば、同じキーが何度も現れていることを「めんどくさい」と感じるはずだ。そして、こんなふうに書けないだろうかと一度は思うはずだ。data = %h{ ['name', 'age', 'email'], ['Foo', 20, 'foo@mail.com'], ['Bar', 21, 'bar@mail.net], ['Baz', 22, 'baz@mail.org'], }これを言語仕様で実現するのか、またはライブラリで実現するのかは正直どっちでもいい。もちろん、このくらいなら我慢するという選択もありだ。大事なのは、「めんどくさい」と感じるセンスがあるかないかである。
ちなみに私も同様の感想を C# 3.0 で抱いたことはあるものの,これを「めんどくさい」と感じるか否かで人を分類することが本質的なのかはよく分からない.学問的なレイヤでの「本質」に比べれば,シンタックスなんて好きにすればよいように思うものの,自分も含めて多くの人々が感じている問題意識はどうも圧倒的にシンタックスレイヤに集中している気がする.やはりコードは文化で,これは文化の対立なのかもしれない.
まあ今回はオチなしなので,最後は F# のコード例で閉め.
#light type MyRecord = { title:string; url:string; pagerank:int; } let record = { pagerank=10; url = "http://www.google.co.jp"; title = "Google"; } let records = [| { title = "Google"; url = "http://www.google.co.jp"; pagerank=10; } ,{ title = "Yahoo! JAPAN"; url = "http://www.yahoo.co.jp"; pagerank=8; } ,{ title = "Live Search"; url = "http://www.live.com/?mkt=ja-jp"; pagerank=8; } |]