Some.apply() は引数1つなんです。

Someの基本コンストラクタ/applyメソッドは引数1つなのにもかかわらず、Some(1, 2) のように複数引数を渡せるかの様に書けてしまうのはなぜなのか。分かりやすく説明してもらったのでそのまとめ。
2
あおい @aoi0308

実践Scala:121ページ目。Some(( p.name, p.age )) って書いてあるけど、これは説明のためで、普通は Some(p.name, p.age) って書くってことでいいのだろうか。括弧2重に書くのが普通? #scala

2011-06-19 22:16:35
kmizu @kmizu

@aoi0308 括弧2重に書かないと、ときに思わぬ罠にはまることが…。というか、本来は括弧2重であるべきなんですよ。Some.applyの引数は2個じゃないので。 #scala

2011-06-19 22:23:10
あおい @aoi0308

@kmizu なるほど、そうなんですね。ありがとうございます。

2011-06-19 22:25:21
kmizu @kmizu

@aoi0308 ちなみに、括弧を二重する習慣にしていないと、Someのパターンマッチのときに必ずはまります(経験談) #scala

2011-06-19 22:38:15
あおい @aoi0308

@kmizu ついでに質問させて下さい。Someは引数1つなのに、複数引数を受け付けられるのはなぜなんでしょう?コンパイルエラーになってくれたほうが素直な気がしますが。

2011-06-19 22:38:23
kmizu @kmizu

@aoi0308 まず、前提として:Someだけが特例なわけではないです。applyメソッド呼び出し && 多相メソッドが重なったときにおきる問題です。 #scala

2011-06-19 22:43:21
kmizu @kmizu

@aoi0308 さて、その上で何が起こるか、というと、Some.apply(100, 200)のような呼び出しにおいて、Scalaの型推論器はA = (Int, Int)のように解釈してしまいます。

2011-06-19 22:49:34
kmizu @kmizu

@aoi0308 つまり、複数引数を受け付けられる、のではなく、1タプルを引数としたメソッド呼び出しとして解釈されてしまいます。 #scala

2011-06-19 22:50:26
kmizu @kmizu

@aoi0308 それでも謎なところはあって、何故、Some.apply(100, 200)の時点で、型推論する前に複数引数のメソッド呼び出しと解釈してくれないのか、という点です。 #scala

2011-06-19 22:53:05
kmizu @kmizu

@aoi0308 で、言語仕様読んでみたところ、一応は、仕様どおりであることはわかりました。ただ、これは実装じゃなくて言語仕様のバグじゃないかという気がしています。 #scala

2011-06-19 22:59:07
kmizu @kmizu

@aoi0308 どういう風に仕様どおりかというと、def f[T](x: T)というメソッド定義に対して、f(x, y)という呼び出しがあった場合の扱いについて、特にこれをrejectするような形で書かれていませんでした。 #scala

2011-06-19 23:01:03
kmizu @kmizu

@aoi0308 詳しくは、 Scala言語仕様 6.6 Function Application に書いてあります。 #scala

2011-06-19 23:05:08
あおい @aoi0308

@kmizu ふむふむ。型パラメータをタプルだと解釈するわけですね。始めの方でapplyメソッド && 多相 っていう言い方をしてましたけど、apply以外でも起こりうるわけですよね?(手元で試せてません)

2011-06-19 23:07:49
kmizu @kmizu

@aoi0308 はい。不正確でした。apply以外でも起こります(試しました)。 #scala

2011-06-19 23:09:27
kmizu @kmizu

@aoi0308 問題の本質は、Some.apply(1, 2)というテキストと、Some.apply(1, 2)という抽象構文木の間のマッピングが明確に定義されていない点だと考えています。ちょっとわかりにくいので、続けます。

2011-06-19 23:10:47
kmizu @kmizu

@aoi0308 まず、パーズ時点で、Some.apply(x, y) は具象構文にしたがっているので無事にパーズできます。しかし、この後の解釈が問題で言語仕様を読む限りでは、Some.apply((x, y))のように解釈しても矛盾が生じないように読めてしまいます。

2011-06-19 23:12:42
kmizu @kmizu

まあ、言語仕様上の理由についてはそんな感じですけど、実装の観点で考えた場合、なんでこんな挙動するのかは簡単に説明がつきます。 #scala

2011-06-19 23:18:07
kmizu @kmizu

たとえば、Some apply (100, 200) と Some.apply(100, 200) というメソッド呼び出しがあったとします。このとき、パーズが終わった後かパーズ中かはわからないですけど、前者は後者の形式にdesugarされているはずです。

2011-06-19 23:20:02
kmizu @kmizu

で、そのせいで、パーズが終わった段階で既に、 Some apply (100, 200) と Some.apply(100, 200) の違いは区別が付かない。ここで、複数引数呼び出しみなしてrejectしてしまうとまずい、何故なら、

2011-06-19 23:21:07
kmizu @kmizu

Some apply (x, y) は Someに タプル(x, y) を渡して apply を呼び出している、と解釈されるので。それで、複数引数かタプル引数かの判断を後のフェーズに任せた結果こうなったのだろうと。

2011-06-19 23:23:03
あおい @aoi0308

@kmizu なるほど、Scalaの糖衣構文上、仕方のない一面があるわけですか・・

2011-06-19 23:30:52