こんにちは、SmartHRでバックエンドエンジニアをやっているsugamasaoです。
9月に行われたRubyKaigi TakeoutはRuby 3.0へ向けてのお話やGC、焚き火など興味深いトークばかりでとても面白かったですね! そして、そんなRubyKaigi Takeoutをサカナにして、SmartHRのエンジニアが色々話す感想戦を開催しました。
先日エンジニアイベント『RubyKaigi 2020 Takeout 感想戦 SmartHR@仮想松本』が開催されるまでというエントリを公開しましたが、本エントリではどのような内容だったのかを中心に紹介したいと思います。
注意:記載内容はイベント当時の内容のため、現在のRubyの仕様と異なる場合があります
はじめに
RubyKaigi Takeout 2020感想戦@仮想松本と題してイベントをconnpassで公開したところMatzさんをはじめ明らかに強メンの参加が観測され、我々はどうなってしまうのか不安で夜も眠れませんでした当日が楽しみで仕方がありませんでした。
結論から書くと、当日の感想戦に用いたトークのうち4つに関して@mametterさん、@_ko1さん、@shugomaedaさん、@yukihiro_matzさんが一緒に感想戦で話していただけるという大変嬉しい事態となりました(予定していたものではなかったので、我々としてもまさかの展開でした)。
そんなわけで想定以上に感想戦で話した内容も充実したものとなったため共有を目的としてこのエントリを書いております。
感想戦① Type Profiler: a Progress Report of a Ruby 3 Type Analyzer
- オリジナル動画 https://www.youtube.com/watch?v=6KcFdQWp8W0
- 感想戦動画 https://www.youtube.com/watch?v=TN5QVTtUHLk&t=495s
当初はSmartHRのエンジニア中心に進行していたのですが、本編動画の再生が終わったあと、@mametterさんがzoomで参加していただけたので直接質疑応答を行いました。
(当初、登壇された方をお呼びするような想定はしていなかったため、コンタクトを取る際にドタバタしてしまいましたが、うまくいってよかった……!!)
以下はSmartHR社のエンジニアが動画を見ながら話した内容です。
- 型情報は書きたくないけれど型チェックは欲しい
- Rubyのコードの中に型情報を書かずに推論してくれるのは嬉しい
- 引数に渡している値から型情報を使って推論していく
- RBSファイルに書かれた型情報と実コードで同じ型情報にしないといけない場合、警告が出るとダックタイピング的な書き方の相性が良くないのかも?
- 別ファイルに型情報を定義すると、ソースコードと行ったり来たりしないといけないのは良し悪しあるかも……?
- 型情報の生成はCIで生成したりするのが良いんですかね。手で修正する必要がある場合もあり得そうなので、どう運用していくのが良いかは今後の課題になるかも。
以降@mametterさんが配信用Zoomにjoinしていただきました。 join前に疑問として上がった内容について回答をメインにお話ししていただきました。
- @mametter「バイトコードを使って解析するのはASTに比べてRubyの複雑な意味論が整理されているため、実装がしやすかった。一方、バイトコードでは失われている情報もあるので、今となって最適解かというと微妙なところ」
- @mametter「バイトコードにすると失われてしまう情報はたとえば、ifとunlessのどちらか分からないなど」
- @mametter「型プロファイラーで生成したRBSファイルに手書きした情報がある場合、手書きした部分は上書きしないようになっているので、該当箇所の型情報が実際に変更があった場合は手書きで追従していく必要があります」
- @mametter「TypeProfilerはRBSフォーマットを出力するようにしていますが、たとえばJSON形式などを出力することを検討しても良いかもしれない」
感想戦② Reflecting on Ruby Reflection for Rendering RBIs
- オリジナル動画 https://www.youtube.com/watch?v=OJRQAn6BfpE
- 感想戦動画 https://www.youtube.com/watch?v=TN5QVTtUHLk&t=3747s
このトークは本編の登壇者の参加はありませんでしたので、SmartHR社のエンジニア同士での会話のみでお送りします。 主にスライドを見ながら使っているメソッドについて感想を言い合う、文字通りの感想戦となりました。
- ある意味このトークも型の話ですね
- このスライドはRubyのソースコードが出てて、見てて動きがわかるので楽しい
- Rubyのソースコードを解析してメソッドやクラス名などを解析していくという内容
- 題材として使っているtapioka gemはSorbetでRBIファイルを作成するためのライブラリ
- TypeProfと違って、Sorbetは実行時にも型チェックをする
- 「Reflection」という言葉を目にすると身構えてしまうけど、Rubyにとっては普通のコードなんですよね
- このスライドはスライド内で動きがあって何が起こってるかわかりやすくて良いですね
- メタプログラミングRubyを読んでるとより理解が深まりそう
- Methodクラスの
parameters
メソッド知らなかった(普段は使わないですね……) - tapioka gemではObjectSpace.#each_objectで定義された全オブジェクトを取得するメソッドを使ってますね
- 今回紹介されたコーナーケースは実業務ではほとんど使わないようなケースだけど、Rubyの世界ではできちゃう(手段がある)のでしょうがないよね……
- Module#remove_constって実在するケースだとどういうときに使ってるんだろう?
- 身近な例だとRails 6.0から入ったZeitwerkでホットリロードするときに使っていたりしますね
感想戦③ Ractor report
- オリジナル動画 https://www.youtube.com/watch?v=40t8EPpnujg
- 感想戦動画 https://www.youtube.com/watch?v=TN5QVTtUHLk&t=6360s
Ruby 3.0の目玉機能の1つ、Ractorについてのトークです。 本編登壇者の@_ko1さんは当初Twitterでリアルタイムに応答していただいたのですが、途中から参加してくださったので@_ko1さんの応答の含めて書いています。
- 元はGuildという名称だったけどゲーム系の開発で一般的に使われる名称だったのでRactorになりました
- JRubyなどはホンモノのThreadで動くのでマルチCPUで動くんですよね
- 以前の職場で、その問題を解消するためにJRubyを使ったことも
- マルチスレッドのような環境でのバグは再現性がないのでテストを書くのも難しいですよね
- (@_ko1さんのTwitter実況より)Ractorという名称についてはReactorと似てるという指摘があります
以降@_ko1さんが配信用Zoomにjoinしていただきました
- @_ko1「Ractorのデバッグつらいんですよ!」
- @_ko1「Actorモデルは定義が難しい。みんな自分の考えたRactorを提唱している」
- Ractorが普及していくことでより関数型っぽいコーディングスタイルになっていったりするんでしょうか?
- @_ko1「Rubyがより関数型っぽくなるかはわからないけれど、Ractorを強く意識した使い方をするとそうなる(そう見える)かもしれない」
- @_ko1「(よりScalaっぽくなるのかな?という質問に対して)Scalaと比べて型に関する機能が不足しているという話がありましたが、Rubyってメタプログラミングが強力なのでアノテーションがないと難しいんですよね。ただ、3.0で型に関する機能などが入ってくるので、少し近づいてくるかもしれないですね」
- Ractorのyieldメソッドは何か由来があるのでしょうか?
- @_ko1「RactorのyieldメソッドはFiberと似ているので、そちらに由来しています」
- Ractorのsendメソッドは既存のメソッドがありますが抵抗はあったのでしょうか?
- @_ko1 「あったけどsendにしたいんですよ」
- Rubyの日本語と英語のドキュメントで異なることが書いてある場合、何を信じれば…?
- @_ko1「ソースを読んでいただくのが良いですね、、、」
- Object#sendの「ライブラリでは
__send__
を使うべき」というような情報の場合、どちらが正しいか把握するのは難しいですね……
- 実際の用途を考えると、クラス変数とクラスインスタンス変数がRactorで使えないことの制限が厳しいんですよね
- @_ko1「クラス変数は現在の仕様が難しいので……。インスタンス変数に関してはもしかしたら制限が緩くなるかも。ただ、現状だと普通の配列を渡すことができないので、インスタンス変数を何もせずに使えるという形にはならなさそう」
- FiberやRactorの生成コストについて
- スライドだとFiberがすごく早いですね
- @_ko1「RactorはFiberに比べるとまだ遅いんですよね。Procくらいパッと作って実行できる用途を考えると遅いので、同じくらいの速度にしたいですね」
- @_ko1「生成コストが軽くなって1万個とかカジュアルに作れるようになって、eachなどでカジュアルに使えるとプログラミングの仕方も少し変わってくるかもしれないですね」
- @_ko1「今後、ある種のProcは送れるように検討はしています」
- @_ko1「Procの外側のローカル変数を触ってしまうと、そのローカル変数経由でオブジェクトの共有が起こってしまうのですよね」
- @_ko1「現在のRactorは制限が厳しくて3.0になって一気に使えるという状況ではないので、使えるところから少しづつ経験を積めればと思ってます」
- @_ko1「例えばSidekiq内でlogをgzip圧縮するなどで使えるのかも」
- @_ko1「まだいますぐ既存のアプリケーションでRactor活用できるようなものではないです。しかし、マルチスレッドで動くように作っているけど実際にはスレッド」セーフになっていないプログラムはたくさんあるのではないかと考えているので、Ractorを使おうとすると制限により安全に書けるようになるのではと考えています
- @_ko1「Ractorのデバッグは大変で、これはC言語でマルチスレッドプログラミングをしているのが問題なんですが、今からRustで実装するというのは厳しいので……」
- (再びRactorの利用例について)ActiveRecordはRailsのクラス変数にアクセスしているので、Ractorに渡すのは難しそう
- @_ko1「たとえば一気にS3へファイルをアップロードするとかは通常のスレッドでも可能なのですが、Ractorを使うと安全に実行できるということを訴求したいですね」
- @_ko1「毎年Rubyが死んだって言われるじゃないですか、それを破るための新しい使い方を提供していきたいんですよね。CPUをたくさん使うシチュエーションは今後増えていくであろうと思っているので、その時にRubyが選択肢になるようにしていきたいんですよね」
- ActiveRecordがSQLに変換する部分やバリデーション、builk_insert時のHash変換などをRactorで実行できると良いのかもしれない
- @_ko1「たとえば一気にS3へファイルをアップロードするとかは通常のスレッドでも可能なのですが、Ractorを使うと安全に実行できるということを訴求したいですね」
感想戦④ Magic is organizing chaos
- オリジナル動画 https://www.youtube.com/watch?v=QKiQiK7gSHQ
- 感想戦動画 https://www.youtube.com/watch?v=TN5QVTtUHLk&t=9828s
この回では開始時点から前田修吾さん(以下@shugomaeda
)が参加してくれました。
このトークではRefinementsをより使いやすくするための拡張の提案について、過去と現在の解説をされていました。
本編動画を流している間はスライドの補足や島根の紹介で鳥取砂丘の画像(!!!)を使っていたことなどを解説していただきましたが、本編動画が終わった後の時間が盛り上がったのでその時会話の内容を抜粋しています。
- Refinementsってみんな使います?
- プロダクションコードではあんまり使わないかも
- 例えば弊社でメンテナンスしているactiverecord-bitemporalだと使っているケースはあります
- Rubyってモンキーパッチ簡単にできちゃう問題
- ライブラリ内でArrayなどを拡張したい場合に影響範囲を抑えられるのでRefinementsを使うことがありますね
- 今回のトーク内容で説明されていたブロック内部でのみ適用させる機能はすごい欲しい
- instance_evalでusingを明示的だとわかりやすくて良いですね
- とはいえ、パフォーマンスまで考えるとたいへん
- instance_execとinstance_evalの違いってなんだっけ
- instance_evalだとレシーバーが渡されるが、instance_execだと指定した引数を渡します
- Refinementsの使いどころって?(再)
- 実はプロダクションコードでも使ってますよ
- PRでosyoさんからこういう場合はRefinements使ったら良いですよってコメントもらったりします
- 他のファイルから参照されたくない処理を匿名のモジュールに定義してusingして使うケースですね
- concernでモジュールを定義して他のモデルにincludeして使うことはよくあるけど、include先で使って欲しくないようなメソッドを定義するときに使ったり
- 本当に参照して欲しくないprivateメソッドを定義するような使い方ですね
- @shugomaeda「Rubyは名前の衝突に弱いので、このように隠蔽することで名前の衝突などを避けられる利点はありますね」
- 本編で解説があったとおり、ブロック内でusingを使う場合にブロック内で暗黙的な拡張が行われると、ユーザーにとってその挙動を初見で把握するのが難しくなる懸念はありますね
- instance_execのようにusingを明示すればわかりやすくなるとはいえ、たとえばユーザー側でブロックを呼び出すたびにusingを明示したいかというと微妙なケースがある
- @shugomaeda「もともとRubyはプログラマーを信頼している言語だと思うので、安全側に倒すよりはおもしろいことをやりたいと思った人が書き味よくプログラムをかけるようにしたいんですよね」
- Refinementsってエッジケースで不具合があったりするので、みんなに使ってもらってもっと良い形にしていきたい
- Refimentsはモンキーパッチをきれいにやるためのイメージが強くて、ユースケースの訴求ができてないのかも
- Kotlinのimport機能のように特定のメソッドだけ呼び出せる機能と捉えれば使えるケースがあると思うんですよねえ
- Refinementsできれい・安全にかけるパターンの知見が溜まるともっと利用例が増えそう
感想戦⑤ Matz Keynote
- オリジナル動画 https://www.youtube.com/watch?v=wVrJZReHlM8
- 感想戦動画 https://www.youtube.com/watch?v=TN5QVTtUHLk&t=12674s
感想戦最後はMatzのキーノートでした。こちらも開始時点からMatz(以下@yukihiro_matz)に参加していただき、トークの内容を深堀りした内容を聞いたりすることができました。
- @yukihiro_matz「言語デザイナーとしては新しいことをしたい」
- @yukihiro_matz「止まると死んでしまうし、つまらない。もともとは趣味で始めてるものなので……」
- @yukihiro_matz「とはいえ好きに進めてしまうと実際のユーザーを置いてけぼりにしてしまう。Ruby 1.9の時は1.8と1.9で断絶が起きてしまった」
- @yukihiro_matz「止まると死んでしまうし、つまらない。もともとは趣味で始めてるものなので……」
- 1.8から1.9の移行についてRuby開発者としては大変でしたか?
- @yukihiro_matz「VMの統合など内部的には大きく変わったので互換性を維持するなどは大変だったが、結果としてその点では互換性の問題は大きくなかった」
- @yukihiro_matz「しかし、そのタイミングで文字コードについての扱いを変えた」
- @yukihiro_matz「1.9は1.8より何倍も早くなったので、少々互換性に問題があっても移行が進むだろうと考えてた」
- @yukihiro_matz「でも5年くらいかかった」
- @yukihiro_matz「今後はそういう断絶を起こしたくないので、Ruby 3.0は互換性を大切にしている」
- @yukihiro_matz「でも5年くらいかかった」
- @yukihiro_matz「VMの統合など内部的には大きく変わったので互換性を維持するなどは大変だったが、結果としてその点では互換性の問題は大きくなかった」
- (MJITの導入について)
- @yukihiro_matz「現在のMJITはGCCを使っていますが、MIRというのを検討しています」
- @yukihiro_matz「セキュリティ要件でコンパイラをプロダクション環境で入れられない場合があり、そうすると現在のMJIT(のGCC)で動かせなくなってしまうので」
- (Racterのsend/recvについて)
- @yukihiro_matz「sendという命名については意見もあったけれど、Socketで使ってるから良いかなと考えてます」
- @yukihiro_matz「receiveってスペルミスしそうだし、UNIX由来の4文字で良いかなーと」*1
- @yukihiro_matz「2.7で入りそうだったメソッドを取り出せる演算子の延長でメソッドを呼び出したりできる演算子が入ると、直接メソッド名を書かなくて良くなって自然な形になりそうという考えはあるけど、まだ記号について良い案が思いついていない」
- (TypeProfについて)
- Rubyは全てのメソッドに戻り値があるのですが、voidを表現するにはどうしたら良いのでしょうか?
- @yukihiro_matz「TypeProfはvoidを発見できないんですよね。なのでvoidを表現したい場合は人の手で表現する必要がありますね」
- @yukihiro_matz「戻り値に関心がない場合は適当な値を返してけば、誰も使わないからそれで良いという世界だったんですね」
- @yukihiro_matz「今後TypeProfが普及した場合、voidはライブラリ作者がこのメソッドの戻り値を触ってくれるな、ということを表明するために使ったりする場面は出てきそうですね」
- Rubyは全てのメソッドに戻り値があるのですが、voidを表現するにはどうしたら良いのでしょうか?
- キーノートが最後だったのは何か理由があったのでしょうか?
- @yukihiro_matz「最初だと思って作ったら最後だった」
- 「RubyKaigi全体の予習になるように作ったのだけど、復習になってしまった」
- @yukihiro_matz「最初だと思って作ったら最後だった」
- 他の言語にもある機能でこれはRubyに入れたらおもしろいかもと思う機能はありますか?
- @yukihiro_matz「入れたい気持ちと入れたくない気持ちがあるけどPythonのimport的な機能はネームスペースの衝突を避けられるという利点があって、require+using的な感じでファイル単位のimport的なことができないかな?と考えたりしています」
- @yukihiro_matz「ソフトウェアトランザクショナルメモリーのアイディアはおもしろいなと思います」
- @yukihiro_matz「ただ、トランザクションの排他制御があるので、書き込みがたくさんある用途だと性能が出にくいなどの問題もありそう」
- Ruby 3.0に向けて警告を出力する問題についてどう感じているのでしょうか?
- @yukihiro_matz「2.7.0で警告出るようにして、2.7のうちに直せば3.0で動くよという移行パスを考えました」
- @yukihiro_matz「ところが、自分のコードではなく使ってるgemから大量の警告が出まくるという問題があって、コードの責任を持っている人と警告を受ける人が違ったということになってしまった」
- @yukihiro_matz「プロダクションコードでたくさん警告が出るのは怖いという意見があった」
- @yukihiro_matz「自分のコードに危険なコードがあるかわかりにくくなる」
- @yukihiro_matz「2.7.2のデフォルトでは警告がでない代わりに警告を出すモードがあるので、たまに自分で確認してね」
- @yukihiro_matz「warning gemを使うと警告を出すディレクトリを指定できるのでうまく使っていきたい」
おわりに
「はじめに」でも少し書きましたが、コミュニティ主導のようなイベントではないにもかかわらずRubyKaigi Takeout本編で登壇された方々に直接お話を聞くことができ、大変嬉しい誤算のあるイベントになりました。 我々SmartHRのエンジニアとしても気になる点について深掘りしたりRubyコミッター視点での肌感などを聴ける貴重な場となりましたが、一方でRubyユーザーが感じる文字通りの感想を直接お伝えできてとても良いイベントになったかなと感じています。
今回のイベントを通じて、さらにRuby 3.0(とその後の進化が)たのしみになりました。Rubyの開発に関わっている皆さん、毎年ワクワクを届けてくれてありがとうございます……!!
■■■
以下は個人の感想です。
感想戦を実施してから本エントリのアップまで時間がかかってしまってすみませんでした;;
YouTubeにアップロードされている動画を見直して、発言内容を纏めながら文章に起こしていたのですが、すごい大変だったので今後は分担してやったりしたいと思います(そして、自分がめちゃくちゃに酔っていくのを時系列で見直すのキツすぎワロタ)。
We are Hiring!
SmartHRでは、こうしたイベントへ積極的に参加したいエンジニアや文字起こしが得意、あるいは文字起こしをテクノロジーで解決してくれるエンジニアを募集しています!(※文字起こしは冗談なので興味なくても大丈夫です!)
https://hello-world.smarthr.co.jp/
選考フローも全てオンラインに対応されていますので、安心してご応募いただければと思います。
*1:注意:Ractor#receiveメソッドも追加されました