2019年11月12日火曜日

拡張メソッドは非常に便利だがPHPとは言語の質的に相性が悪い

PHPに拡張メソッドを実装しよう、というRFCが挙がっていたが、猛反対により却下になったらしい。以下は rana_kualu氏の解説。

PHPでメソッドを動的に生やそう - Qiita

個人的結論から言えば、PHPは恐らく「言語の質的」に拡張メソッドとは相性が悪い。
それが反論に現れてる。故に反論の理由もある程度忖度はできる。

反論への忖度


・extendsしたあとで基底クラスにメソッド追加したら死ぬ。


死にません。が言いたいのは判る。
PHPは、モジュール間結合がランタイムかつincludeである、というキモい特徴があり、実行するその瞬間まで、そのメソッドが何処の何者なのか、マジで解らないという駄目な特徴がある。
故に、PSR1やらPHPDOCで「運用で回避」してるわけだ。

それをこれ以上脅かすなと、そういうことだろう。

ちなみにC#では、拡張メソッドはnamespaceの中で定義するものなので、それを指定しないと使えません。

・privateを簡単に見れるようになってカプセル化が死ぬ。


死にません。拡張メソッドに対する誤解。本来の拡張メソッドはprivateを叩けません。関数スタイル呼び出しのsyntax sugerなだけ。

ただカプセル化==privateはちょっと考え方が古いかねえ。その証拠に最近の新言語は、クラス外での関数定義を基本構文としてる。rustとかgoとかがそうだ。

・JSじゃないんだから普通にextendsすりゃええやん。


実際その通りなんだが、本当にそれが可能な場合だけですか?って話なわけさ。arrayをこねくり回す場合って多いでしょ。みんな不満ないのだろうか。

・Kotlinのはただの糖衣構文であって、実際にクラスを変更しているわけではない。

ソーですネ。だから複雑化しないはず。

・複雑化するリスクに対してベネフィットが少なすぎる。

しません。そもそも乱用したいわけではないので。

・バックポートに使えそう。たとえばSplFileObjectはPHP5.1.0で実装されたがSplFileObject::freadはPHP5.5.11で追加された。

そうそう。そういう感じで使うのがただしい。オリジナル(バイナリ)をいじらずに、何か処理を足すのは、拡張メソッドの本来の遣い方に近い。そこextendsでいいやとか言われちゃ。

・runkit_method_addやuopz_add_functionで同じことができるよ。

その通りだけど、プロダクション版でそんな冒険したくないわけさ。

断言:拡張メソッド自体は便利なのは間違いない※PHPは追いといて

クラスという「カタチ」にこだわりすぎなんだよね。本来拡張メソッドというものは「任意の型」にメソッドを足せる。どういうことか?

典型的な例でいえば、配列にもメソッドを足せるということだ。ただこれもPHPのではarrayの中身の型を担保する手段がないので、メリットは薄い。まあジェネリック型のタイプヒントをサポートしてからだろう。そして多分その日は来ない。

  • array_search($needle,$haystack,TRUE) !== false #とか毎回書くのバカバカしくね?
  • $haystack->hasvalue($needle) # の方が「解りやすい」でしょ?

と小生は思うんだが。

でも総合的には、どうやらPHPの内蔵機能に皆さん不満がないってことなんだろうなあ。

事実、同じ動的言語であるはずのRubyには拡張メソッドがある。

[Ruby]他のクラスを拡張するいろんな方法 - Qiita