function 〜 が式になるか文になるかはどうやって決まる?

引き続きKanasan.JSで出た話題。
ECMA仕様の文法を眺めてみて気づいたことを書いてみます。

今まで「文を書ける文脈では文になり、式しか書けない文脈では式になる」とか勝手に思っていたのですが、ひどい誤解でした。

function が式か文かどうかは,その前が文のおわりかどうかで判断

というのも微妙に違います。



まず重要なことをひとつ。
(ECMAScriptの)文法上は、function文なんてものは無いです。*1

文(statement)の定義がいろいろ書いてありますが、この中に関数定義を行う文はありません。

じゃあ関数定義はどう定められているかというと、トップレベルを見ると

  • プログラム = SourceElementのリスト
  • SourceElement = 文(Statement) or 関数宣言(FunctionDeclaration)

となっています。あと関数のbodyもプログラムと同じくSourceElementのリストです。

このSourceElementが書ける場所に書かれたfunctionは関数宣言になります。これがつまり、「functionがどう解釈されるかは直前がSourceElementの終わりかどうかに依存する」ということなわけですね。

ここで、この関数宣言を『function文』と呼ぶのはちょっとまずい気がします。なぜなら文が書ける場所は他にもあるからです。例えばifの中では関数宣言は書けません。

個人的には、これがCの負の遺産のような気がしてなりません。こんなものやめてしまって var f = function式; で統一すればいいのに。OCamlみたいに、 var f(x){...}; を var f=function(x){...}; のシンタックスシュガーにするとかして。

関数宣言を文法から無くそうとすると、どんな問題が出るでしょうか? 後方参照が多少制限される*2ぐらいだと思うのですが。

function がどう評価されるかは、その後ろには依らない。

>>> (function a(){}, 3);
3

括弧内は有効な式ですので、ちゃんと評価値が返ってきます。
しかし外側の括弧を取ると…

>>> function a(){}, 3;
syntax error

このようにパーシングに失敗します。

無名関数宣言?

よく分からないこと。

>>> (function(x){return 1}(2))
1

無名関数を2に適用しています。
外側の括弧を取ると、

>>> function(x){return 1}(2);
2

これはどうやら、function(x){return1} が関数宣言として処理され(無視され)、続いて(2); が単体の式文として処理されているのだと思うのですが、関数宣言は identifier 必須で無名関数は許していないはずなんですよね。これは仕様に合致した挙動なのでしょうか?

解決

nanto_viさんの昔の記事を読んで納得。
JavaScript1.5にはFunctionExpressionStatementsがある!! (驚愕)

それから曖昧にならないように次のようになっている。

Block と曖昧になることから、 ExpressionStatement は大括弧 "{" で開始することはできないことに注意。また、 FunctionDeclaration と曖昧になることから、 ExpressionStatement は function キーワードで開始することもできない。

12.4 式文 (Expression Statement)

*1:追記: (Mozillaの)JavaScriptにはあります>

*2:相互再帰とかは書けるので別に困らないんじゃない?