FunctionsというRFCがありました。6が、なんかV2として復活してました。今回は提案者として重鎮Nikitaが参戦しています。NikitaはPHPのコア開発者のひとりで、記憶に新しいところではプロパティ型指定を作った人です。コードもできていてプルリクが出されています。RFCの提出が2019/03/12で、投票は2019/04/17開始・2019/05/01終了です。なんでそんなスケジュールきつきつなのだ。2.0IntroductionPHPの無名関数は、単純なことしか行わない場合でもやたら冗長になる場合があります。使用する変数を毎回手動でインポートしなければならないなど、構文に定型句が多いためです。このせいで、簡単なクロージャですら読みづらいものとなってしまいます。このRFCでは、上記の問題に対してより簡潔な構文を提案します。オーバーヘッドの例として、オンラインで見つけた関数を考えてください。$keys);}クロージャが行っている$arr[$x]はたいして難しい内容ではありませんが、定型句のせいで少々わかりづらくなっています。アロー関数を使うと以下のように書けるようになります。$keys);}かつて提出された~>を使うRFCは投票の結果却下されました。このRFCは、却下されたRFCにおいて持ち上がったいくつかの懸念を解消しています。このRFCではさらに、様々な代替構文についての詳細な説明も含まれています。残念ながら、クロージャの短縮構文については、構文や実装に対する制約上の問題で、完璧な解決策を見つけることはできそうにありません。このRFCでは、その中でも一番ましだと思われる選択をしています。クロージャ短縮構文の議論は完全に停滞しているため、今後何年も塩漬けしておくよりも、ここで妥協した方がよいでしょう。Proposalアロー構文の基本形は以下のようになります。expr式中で使用されている変数が親スコープ内で定義されている場合、暗黙的に値渡しされます。次の例では、$f1と$f2は同じ動作になります。$y;};ネストもできます。$z;外側の関数fn($x)は暗黙的に変数$zを取り込みます。内側の関数fn($y)も暗黙的に変数$zを取り込みます。全体として、$zは内側の関数からでも利用可能です。signaturesアロー構文では、引数と戻り値の型、デフォルト値、可変長引数、リファレンスなど、既存の関数と同じ機能を使用できます。以下は全て有効なアロー関数の構文です。functions通常のクロージャ同様に、クラスメソッド中では$this、遅延静的束縛が自動的にバインドされます。通常のクロージャではstaticキーワードでこれを打ち消すことができるため、アロー関数でも同じ機能をサポートしています。}}staticクロージャは滅多に使用されません。これは主に、GCの発生を予測しづらくする$thisのバインドを防ぐために使用されます。ほとんどのコードはこのようなことを意識する必要はありません。この動作について、実際にクロージャ内部で$thisが使用されている場合のみ$thisをバインドするように実装することは可能です。GCを置いておいても、この変更によって動作が変わることはありません。残念ながら、PHPは暗黙のうちに$thisを使用することがあります。Fooと互換性のあるスコープからFoo::bar()を呼び出した場合、$thisが継承されます。我々は内部仕様を知っているため$thisがどうなるか分析できますが、ユーザにとっては$thisがどうなるかは予測不可能です。そのため、常に$thisを使用する通常のクロージャを使うことを勧めます。binding既に述べましたが、アロー関数は変数束縛を値渡しで行います。これは、関数内で使用している全ての変数にuseをすることとほぼ同じです。従って、値渡しバインディングの値をスコープ内から変更することはできないということになります。1バインディング方法の検討については、discussionセクションを参照してください。変数の暗黙的な使用と明示的な使用には僅かな違いがあります。暗黙的な使用では、変数未定義のE_NOTICEは、変数バインド時には発生しません。以下のコードで発生するエラーは、$undefをバインドするときと使用するときの2カ所ではなく、使用するときの一カ所だけです。$undef;$fn();その理由としては、変数が読み取りのために使用されているのか、参照返り値のために使用されているのかを常に判断することができないからです。次にやや人為的な例を示します。0)この$matchesはpreg_matchによって代入されるので、アロー関数より前に$matchesが存在している必要はありません。この場合はエラーを出力すべきではありません。最後に、この自動バインドは文字列リテラルで渡された変数にのみ行われます。以下のコードでは関数内で$xというリテラルが使用されていないため、$xが未定義であるというE_NOTICEが発生します。$$y;この問題は、使われている変数だけをバインドするのではなく、全ての変数を束縛することによって解消することができます。不要だと思われるためここでは対応していませんが、それが必要だという声が大きければ変更されるかもしれません。Precedence=>の優先順位は最も低くなります。=>の右側は全て=>の中になります。Changes残念ながら、fnはトップレベルのキーワードでなければならず、そしてfnは予約済ではありません。ToviloがGitHubのトップ1000リポジトリを調査してfnの使用を調査しました。概要として、fnの使用は全てテスト内であるか、名前空間内での使用でした。Examples以下の例は旧RFCからのコピペです。$c);定型文が44文字から8文字に減りました。$names));31文字から8文字に減りました。多くのライブラリで見られる関数も、以下のように書けるようになります。非常に簡潔で、JavaScriptなどの多くの言語で使用されています。PHPにおいてこの構文は問題が多く、使用することは困難です。このセクションではアロー関数のために考慮されたいくつかの構文について、利点と欠点を解説します。$y最も一般的であり、そしてPHPでは不可能な構文です。この構文最大の問題が、配列やyieldなどにおいてキーと値のペアを指定する目的で=>が既に使用されていることです。従って、以下のコードの=>は配列宣言なのか、アロー関数なのかを特定することができません。$y,];しかし、この曖昧さ自体は問題ではありません。その他の規則によって一意に解決できます。今回の場合は、後方互換性を保つため、上記の構文は配列宣言であり、アロー関数は以下のようにする、と定義すれば一意にすることはできます。$y),];同じ問題がyieldにも存在します。実はアロー関数がなかったとしても、既に解釈が曖昧になる構文は存在します。$yには、さらに次のセクションで解説する問題もあり、そして実はそちらが致命傷です。$y、あるいは類似の構文についてです。$yのような単純な構文であればサポートも可能ですが、==>の左側に任意の構造を記述できるようにすると、パーサの実装が非常に困難になります。$a))))以下構文のパース処理がとってもたいへんで、解決するにはパーサを入れ替えるレベルの改修が必要だ的な記述が延々書かれているのですが、細かすぎてよくわからないので省略。$y上記の構文候補に関する最大の問題は、(の対となる==>が見つかるまではアロー関数であるかそうでないかを調べ続けなければならない、ということでした。明確な解決策は、特有の先行記号を持つように構文を修正することです。このRFCでは、短くて読みやすい候補としてfnを提案しています。問題点は、fnが予約語ではないことです。先行記号のシンボルについては、もちろん他の候補もあります。未使用の単項演算子というパンドラの箱を開けたら。$y実際に実現可能であろう文法は最初の4例でしょう。fnは今回提案されているものです。fucntionは既にキーワードとして使用されているので安全です。不利な点はキーワードが長いということで、そしてアロー関数のセールスポイントのひとつが短いということです。$yはHaskellのラムダ構文と類似の例で\はプアマンズλです。^はC言語でサポートされています。先頭に記号が付いた構文を使うとなれば、=>も邪魔なので消したくなります。$yとは書けないでしょうか?残念ながら、返り値が曖昧になるためこれは不可能です。(\T\T)名前空間の空白サポートをやめ、名前空間は単一トークンと字句解析すれば、この曖昧さは解決可能です。それは破壊的変更になってしまいます。arrows=>のかわりに->や-->を使おうという提案がありました。これらは別の既存構文と衝突します。->はプロパティアクセスで既に使用されています。↓と同じ$x->{$x}-->は--と>の組み合わせです。$x必ず括弧を使うと定義した場合、-->は使用可能です。($x)--は現在のところ正しい構文ではないからです。いずれの記号も先行記号と組み合わせれば使用可能ですが、そうするのであれば=>でも同じことです。separatorsRustなどいくつかの言語は、クロージャのパラメータに異なる種類のセパレータを使います。$y|は有効な単項演算子ではないため、先行記号と同じ区別に使用できます。typesやビット演算子、デフォルト値などで使用されています。$x構文上の曖昧さはないと思われますが、意味を読み取るのは困難です。|$x|のような構文はPHPとしては異質でしょう。syntaxこれまでに解説したものとは全く異なるアプローチとして、RubyやSwiftで使用されているブロックベース構文があります。考えられる構文は以下のようなものです。}先頭に{がありますが、PHPは独立ブロックとしての{の使用をサポートしているので、先行記号としては使えません。以下は正当なPHPコードです。};すなわち、構文解析の発散問題がやはり発生することを意味します。この場合は簡単な回避策があります。式文としてのクロージャ構文を禁止することです。単独のクロージャは許可されず、何らかの式の一部である必要があるということにします。OKこれでブロックベース構文は汎用的に実行可能になります。個人的には、これがfn()より優れているとは思えず、特にアロー関数をネストする場合はさらにややこしくなります。syntaxC++11では、以下の構文でラムダを使用可能です。cpp[captures](params){body}PHPの場合は[$x]($y)が既に有効な構文であるため、この構文は使用できません。expr構文も考えられました。PHPではこの構文は曖昧です。behaviorアロー関数に関するもうひとつの議論点はバインディングです。アロー関数は親スコープに存在する変数を自動的にバインドします。問題はそのバインドをどのように動作させるべきかです。基本的に3つの選択肢があり、それぞれを値・参照・変数によるバインディングとします。(&$x)に相当します。参照バインディングの利点はアロー関数内で変数を変更できることです。残念ながら、2つの大きな問題のため、参照バインディングが値バインディングより優れているとは言えません。ひとつめは、参照バインディングのためには参照ラッパーの作成と参照が必要となるため、パフォーマンスが低下するということです。アロー関数と通常クロージャを選ぶときの基準が性能差であるとなれば、それはとても残念なことです。ひとつ重要な問題が、クロージャの内側から外側の変数を変更することができるのと同時に、クロージャの内側の変数を外側から変更することもできるということです次の例では、なぜこれが問題になるのか、暗黙の参照バインディングがいかに直感的でない結果になるのかを説明します。5アロー関数が値バインドである場合、全て正常に動作します。参照バインドの場合、以下のように動作します。最初のループでクロージャ内の$iがforeach内の$iへの参照にバインドされます。2回目のループで、この参照先の値が上書きされ、さらに新しいクロージャで$iにバインドされます。ループが終了した後、全てのクロージャはひとつの参照を共有します。値は最後に割り当てられた値です。今回は議論されておらず、PHPでは使用されていない3番目のバインドが、変数バインドです。これこそが本当のスコープバインディングで、クロージャ外の変数とクロージャ内の変数は共有されています。これは参照バインドと同じように見えますが、次の例が示すように全く同じではありません。5参照バインドをリファレンス付きでforeachすると動作が変わります。参照によるforeachは、値の代入ではなく、参照の代入を実行します。これにより、前ループの参照関係は解除されます。各クロージャは対応する配列要素を参照する個々の参照を取得することになります。変数バインドを使用した場合、foreachの呼び出し方は問題になりません。外側の$iとクロージャ内の$iは文字どおり完全に同じなので、クロージャが呼び出された時点の最終的な$iの値だけが意味を持ちます。変数バインドはPHPでは実装が難しく、値バインドと同程度の性能を出すことは不可能かもしれません。このような問題があるため、PHPで実装可能な唯一のバインディング形式は値バインドであると私は考えています。下記のブロックベース構文がサポートされたような場合は、以下のように明示的に参照バインドを許可すると有用かもしれません。Scope将来は以下のように拡張される可能性がありますが、必ずしも推奨されるわけではありません。bodiesこのRFCでは、アロー関数は暗黙的に返される式をひとつだけしか持つことができません。他の言語ではブロック形式で複数の式を受け入れるのが一般的です。expr;}この構文は優先価値が低いため、このRFCでは省略されています。この構文をサポートする利点は、単一の式を使用するか複数のステートメントを使用するかによって2つの異なる構文を使い分けるのではなく、常に単一のクロージャ構文を使用できるようになることです。modeデフォルトでは値バインドを使用しますが、参照バインドもできるように構文を拡張することもできます。bodiesと組み合わせると特に役立つでしょう。構文は以下のようになると思われます。int(2)あるいは、基本的に値バインドを維持し、参照バインドしたい変数は明示することです。int(3)この例では$bは値バインドであり、$aは参照バインドとなります。この構文は既存のクロージャ構文に近いため、混乱を招く可能性があります。通常クロージャ構文では$bはバインドされません。functions通常の関数やメソッドにもアロー関数を使用できるようにするのもよいかもしれません。getterのような単一定型文を減らすことができるでしょう。$this->bar;}対象バージョンPHP7.4です。7.4の新機能多すぎるんだけど大丈夫なのこれ。投票2019/04/17に投票開始、2019/05/01に投票終了。有権者の2/3+1の賛成で受理されます。2019/04/22時点では賛成36、反対7で、よほどの問題でも発生しないかぎりPHP7.4で導入されます。感想JavaScriptのアロー関数って個人的に好きじゃないんですよね。普段スコープがー副作用がーとか言ってるわりに、どうしてこれが平気なのか理解に苦しむ。PHPのアロー関数は上記のとおり値バインドであり、このような問題は発生しません。1しかし、そういう意思を持った上で値バインドを選んだ、というわけではなく単に実装上の理由というのは少々もにょる。これまでのPHPの文法とはかけ離れた異質な構文なので、慣れないと扱いづらそうです。PHPの場合、=>は既に別の構文で使用されています。パーサによるパースに対しては=>を使っていても誤解が生まれないようになっているのですが、しかし人間によるパースが誤解しやすいことは間違いありません。既存構文とは全く互換性がないのだから、人力パースにも優しい新たな演算子を設けてほしかったところですね。あと全く関係ないのだけど、名前空間にスペース空けられるってのをこのRFCで初めて知ったよ。