2022-12-25 (Sun) [長年日記]
_ ...を*, **, &の構文糖にすると言ったな、あれは嘘だ
Ruby Advent Calendar 2022の25日目の記事です。
昨日は羽角さんのirb.wasmの入力メソッドをSTDIOからRelineへ改修した話でした。
前回のあらすじ
前回の記事で、
Ruby 3.2で
*と**が導入されましたが、当初は...と併用されることを想定しておらず、すこしいびつな仕様になっていました。...は*, **, &の構文糖であるというシンプルな仕様に修正しました。
と言っていました。ところが……
遅くなった?
松田さんから*, **や...による委譲が遅いというissueが報告されました。
forwardableを使った場合に比べて2倍以上遅いとのこと。forwardableが何でそんなに速いかというとruby2_keywordsを使っているからで、前回の記事でも書いたようにFeature #19134の修正前は
def foo(...)
bar(...)
end
は
ruby2_keywords def foo(*, &)
bar(*, &)
end
の構文糖でした。正直ruby2_keywordsという盲腸みたいな機能で速いのはずるいと思うんですが……。
修正案
実際のアプリケーションではそれほど影響はなさそうな気もしましたが、Railsでたくさん使われているらしいということで、修正案として、...と*/**の併用を禁止するという提案をしました。
&についても禁止してもいい気もしますが、Ruby 3.1で併用できていて互換性の問題があるということでこちらはそのまま。
ささださんによる最適化
リリースまで時間もないので上記の修正を行うつもりでまつもとさんの返事を待っていたところ、ささださんが最適化してみるのでちょっと待って、ということになり、休暇中のはずのささださんによってpull requestが作成されました。
f(*a, *kw) の呼び出し規約を変えるという結構インパクトのある修正で、リリースまで12日しかなくてYJITチームの何名かはもうすぐ休暇に入ってしまうという話もあり、Ruby 3.2での導入は見送られました。
この最適化自体は、...の仕様をどうするかに関わらず有用なので、おそらくRuby 3.3で取り込まれると思います。
修正
ということで、結局修正案のとおり、...と*/**の併用を禁止するように修正しました。
修正中、以下の部分が最初抜けていてテストが通らずはまりましたが、元のなかださんのコードでは#define idFWD_KWREST 0していたのでこの#ifdefは不要だったという罠でした。
args_forward : tBDOT3
{
/*%%%*/
+#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ $$ = 0;
+#else
$$ = idFWD_KWREST;
+#endif
/*% %*/
/*% ripper: args_forward! %*/
}
今回の修正でFORWARD_ARGS_WITH_RUBY2_KEYWORDSが#defineされていなければruby2_keywordsを使わなくなったので、Ruby 3.2でささださんの最適化が入ったらそうするとよいように思います(が多分忘れる)。
まとめ
Ruby 3.2で...を*, **, &の構文糖にすると言っていましたが、そうはなりませんでした。