RubyKaigi Takeout 2020 の延長線で盛り上がってた Symbol#to_s について
RubyKaigi Takeout 2020 お疲れ様でした。
今回はオンラインということではじめて参加したんですが興味深い内容の話があって楽しかったです。
特に sawanobori からのサンダル紹介、焚き火で飯を炊いて魚を焼くコミッタの日常配信はニューノーマルを感じました。
いやー Ruby コミッタともなると過酷な環境で開発する技術が必要なんですねー勉強になります!
あ、あと Refinements はとてもすばらしく最高な機能なので皆さんどんどん使っていってニューノーマルになっていきましょう。
さてさて、まだまだ RubyKaigi 熱は冷めやらぬのですが今回は 1日目の Ruby Committers vs the World で盛り上がってた Symbol#to_s
の話について簡単にまとめてみました。
Symbol#to_s
で frozen String を返す提案
ちょうど去年の今頃 #to_s
で返す文字列を frozen
にしようという提案がされました。
このチケットで本題の『Symbol#to_s
の戻り値 frozen にしよう』という話が出てきました。
また、このチケットでは Symbol#to_s
だけではなくて以下のメソッドも frozen String を返す変更が含まれています。
Module#name
TrueClass/FalseClass#to_s
NilClass#to_s
関連する PR や経緯などは以下のリンクを参考にしてください。
- まつもとさんのコメント: Feature #16150: Add a way to request a frozen string from to_s - Ruby master - Ruby Issue Tracking System
- Make Symbol#to_s return a frozen String, the same String for a given Symbol by eregon · Pull Request #2437 · ruby/ruby · GitHub
- [EXPERIMENTAL] Make Module#name return a frozen String by byroot · Pull Request #2487 · ruby/ruby · GitHub
- [EXPERIMENTAL] Make NilClass / TrueClass / FalseClass #to_s return a frozen String by byroot · Pull Request #2492 · ruby/ruby · GitHub
この変更はまつもとさんから承認され、マージ後に Ruby 2.7 preview2 で『実験的な機能』としてプレリリースされました。
Ruby 2.7 で変わるはず『だった』 Symbol#to_s
Symbol#to_s
が frozen String を返すようになってパフォーマンスが上がってよかったよかった。
で、終わればよかったんですが Symbol#to_s
の変更をマージしたらいずれかの gem で問題があることがわかりました。
例えば pry
では次のように Symbol#to_s
の戻り値を破壊的に変更するコードが書かれていて影響が出ていました。
def method_missing(method, *args, &block) meth = method.to_s if meth.end_with?('?') # to_s の戻り値を破壊的に変更しているので frozen String だとエラーになる… meth.chop! present?(meth) || present?(meth.tr('_', '-')) else super end end
このように Symbol#to_s
の戻り値を破壊的に変更しているコードはいくつか報告され、また Ruby 2.7 ではキーワード引数周りの対応もあったので混乱を避けるために Ruby 2.7 では Symbol#to_s
の対応は見送られることとなりました。
また、Revert されたのは Symbol#to_s
の変更だけで以下のメソッドは frozen String を返すように変更され Ruby 2.7 の機能としてリリースされました。
Module#name
TrueClass/FalseClass#to_s
NilClass#to_s
1年の時を経て…
先月開催された RubyKaigi Takeout 2020 の Ruby Committers vs the World の延長戦でちょうど Symbol#to_s
の議論がされていました。
そこでも Symbol#to_s
をどうするのかいろいろと議論されていたんですが 1つの結論として Symbol#to_s
を非互換にするのではなくて Symbol#name
という別名のメソッドを追加する流れとなりました。
そしてその当日に Symbol#name
が開発版へとマージされました。勢いある。
#16150 の時系列
- 2019/09/07:チケットが立つ
- 2019/09/19:まつもとさんが承認
- 2019/09/26:
Symbol#to_s
とModule#name
の変更マージされる - 同日:この変更により影響が受けている gem の報告がされる
- 2019/09/27:
NilClass#to_s
とTrueClass#to_s
FalseClass#to_s
の変更マージされる
〜〜〜 - 2019/10/22:Ruby 2.7.0-preview2 リリースされる
〜〜〜 - 2019/11/01:Heroku で問題があると報告が上がる
- 同日:まつもとさんが Revert すると判断
- 2019/11/05:Revert された
- 2020/09/04:frozen String を 返す
Symbol#name
が入った - 2020/09/05:ActiveSupport の
HashWithIndifferentAccess
でSymbol#name
を呼び出す PR が投げられる
〜〜〜 - 2020/12/25:Ruby 3.0 がリリースされる(予定)
まとめ
と、言う感じで Symbol#to_s
問題について簡単にまとめてみました。
結果だけ見れば影響範囲が大きい非互換だったんですが、実際にリリースしてみるまでわからない問題が存在するのはある程度は仕方ないのでむずかしいですねえ。
むしろ今回のケースではプレリリース版で問題があることがわかったのでプレリリースとしては正しい動きをしていた例とも言えますね。
正式版で問題になる前にわかってよかったよかった。
みなさんも Ruby 3.0 preview 版が出たらどんどん使ってみて問題がないか試してみましょう。