初参加&初登壇の RubyKaigi Takeout 2021 で Ruby のマクロの話をしてきたよレポート

書こう書こうと思って気がついたら1ヶ月立っていましたこんにちは。
(去年を除いて)今回が RubyKaigi 初参加&初登壇ということでいろいろと書き残しておこうと思います。
ちなみに今週末に以下のようなイベントで雑に話すので興味がある方がぜひぜひ参加してみてくださいー。

ちなみに『Ruby のマクロ』という主語がクソデカなんですがあくまでも『この登壇では』という前提になります。

Use Macro all the time ~ マクロを使いまくろ ~

動画

www.youtube.com

補足

  • RubyVM::AST はバージョンごとに非互換
    • Ruby 2.6 -> 2.7 で :ARRAY -> :LIST に命令が変わったのがでかい
    • 現時点で存在してるバージョンはすべて対応している
  • Ruby 3.1dev では RubyVM::AST::Node から元のソースコードを取得する機能が入った
    • ただし『配列形式の AST』は依然として復元することができない
    • それはそう
src = <<~EOS
if hoge
  puts hoge + foo
end
EOS

# keep_script_lines を true にすると
node = RubyVM::AbstractSyntaxTree.parse(src, keep_script_lines: true)

# #source メソッドでコードを取得できるようになる
puts node.source
# => if hoge
#      puts hoge + foo
#    end

Ruby でマクロを実装しようと思った経緯

元々は1年ぐらい前に『ブロック内のコードをテキストで取得したい』ということをやりたくて最初はこんな感じで iseq からブロックが定義されているファイルや位置情報を取得してきて実際にそのファイルからソースコードを取得する実装を書いていました。
最初はこれを使っていろいろと遊んでたんですがファイルを読み込む必要がある都合上どうしてもパフォーマンスが気になっていました。
そこで RubyVM::AbstractSyntaxTree.of を利用してブロックから AST を取得し、それを元にして Ruby のコードを復元しよう!としたのが rensei-gem をつくりはじめたきっかけですね。
しばらくは『AST から Ruby のコードに変換する』を目的ととして rensei-gem をつくっていたんですが『あれ、AST を好きに変えたら Ruby のコードの意味を変えれるのでは…?』『これってマクロでは…?』などと考えるようになりました。
元々は Rust のようなマクロが Ruby にもほしいな〜〜〜と思っていて Rust のマクロを調べていた事もあったんですが Rust のマクロがまさに AST レベルでコードを変更するような機能になりますね。
とはいえ構想だけで1年ぐらい何もしていなかったんですが今回の RubyKaigi をきっかけに実際に実装してみた、って感じですね。

RubyKaigi への登壇のきっかけ

最初は RubyKaigi に登壇する事は考えてなかったんですが知り合いが CFP を出すという事で『じゃあ、わたしも出してみるか〜〜〜』というのが直接的なきっかけになります。
Ruby のマクロの話も前々からどこかではしたいと思っていたのでそれで CFP を出してみたって感じです。
なので CFP を書き始めたのは結構ギリギリでしたね。
具体的には CFP の締切が6月末で、6月23日から CFP を書きはじめました。
時系列的には以下のような感じですね。

全体的な時系列

  • 2020/07〜08頃:この頃から AST -> Ruby に変換する実装のたたき台を書き始める
  • 2020/09〜:各 AST の種類から Ruby のコードに変換する処理をチマチマ書き始める
    • 1日1対応する、みたいなことをしていた記憶
  • 2020/12/01:Ruby Advent Calendar で AST -> Ruby のコードに変換する記事を書く
  • 2021/06/23:知り合いに触発されて RubyKaigi の CFP を書き始める
  • 2021/06/30:RubyKaigi の CFP 締切
    • 知り合いに翻訳のチェックなど行ってもらいだいぶ助かった…
  • 2021/07/01:CFP の結果はまだだったがすぐに作業を開始
    • Scrapbox を用意
    • Scrapbox に TODO リストや作業ログ、たたき台などを残していった
    • 最初は rensei-gem の残り作業を拾うところから
      • 無限に Rensei のバグ修正をしていた
  • 2021/07/05:Scrapbox に日報を書くように開始
    • 今回これをやったのがかなりよかった
    • ほぼほぼ毎日書いてた
  • 2021/07/14:CFP が Accept されたと連絡をもらう
    • 録画かリアルタイムか選択できて無限に悩み始める
      • 内容が膨れることはわかっていたので録画の方がよさそうだなあ、と思いつつリアルタイムの方がギリギリまでスライドをかけるので…
      • 結局リアルタイムを選択
  • 2021/07/14:ここから『Ruby のマクロとは…』を考え始める
    • Rensei のバグ修正に終わりが見えずにこのままではやばいと気づき始める
      • Rensei のバグ修正は最悪直ってなくても大丈夫ではあった
    • この時にはじめて kenma-gem(Ruby のマクロを実装する gem)のたたき台を書き始める
  • 2021/07/23:スライドのアウトラインを考え始める
  • 2021/07/28:kenma-gem の仮実装が固まる
    • 一段落したのでここからしばらく虚無が続く…
    • 仮実装を知り合いに見てもらいつつ意見をもらい始める
  • 2021/08/01:作業再開
    • kenma(仮実装)を gem 化した
  • 2021/08/11:実際に kenma-gem でオレオレマクロを定義しつつ課題を探し始める
  • 2021/08/23:スライドを書き始め、ここで Scrapbox のログが途絶える
    • ここからしばらく(プライベートで)超絶つらい期間が続く…
    • 何も記憶がない
    • ひたすらスライドを書いては知り合いに壁打ちをお願いしてたような…
  • 2021/09/11:登壇当日
    • なのか無事に終わらせた…

スライドに関して

スライド作成はいつも Reveal.js を使っていたんですがもうちょっとリッチなスライドをつくりたくて今回 slidev を使用してスライドをつくりました。
最初は slidev でいい感じに作れるやろ〜〜〜と軽く考えてたんですが実際はスライドを作っていた期間のうち半分は slidev と戦っていましたね…。なかなかいい感じにアニメーションとか作ることができなくて…。
スライド自体もなるべく前提知識がなくてもわかるように構成しようとすると無限にスライドが膨らんでしまいどこを削るのかかなり悩んでいました。
RubyVM::AST の説明は最後まで入れるべきかどうか悩んでいましたねえ…。今思うと入れなくてもよかったかなあ…。
本当は rensei-gem や kenma-gem の実装の話とかもしたかったんですが今回はあくあまでも『Ruby のマクロとは』という店にフォーカスを当てた内容にしました。
実装に関してはまたどこかで機会があれば話してみたいですねえ。

所感

はい、ということで RubyKaigi 初参加で初登壇する実績を解除しました。
今回はじめての参加だったので当日まで本当にどうなってしまうのかめちゃくちゃ不安だったんですがなんとか(登壇も時間どおり)無事に終わらせることができたのでそこが一番よかったです。
登壇自体は事前にかなり練習していたのでそれはやってよかったです。
内容的には結構ウケてたんですかね?あんまり実感がなくてよくわからないんですが終わった後に特に話を聞くこともないのでまあそんな感じだったのでしょう。
そういう意味では手応え的なのは全然なかったですねえ。
それはそれとして個人的には言いたかった事は全部言えたと思うので満足感はかなりあります。
マクロの機能自体も当初から想像していた形にほぼほぼできたと思うのでそういう意味だと達成感がありすぎて逆にやる気が虚無になっていますね…。
登壇でも言ってたんですが、ファイル単位でガッツリ書き換える、っていう運用はまだまだ先だろうけど局所的にマクロを使うのはそんなにハードルが高くないとは思っているのでそのあたりは実用的にしてみたいなあ、という気持ち。
あとマジで Scrapbox にログを残すようにする運用はよかったので今後も利用していきたい。
最後に運営の方々、マクロ実装・登壇の壁打ちに付き合ってくれた方々ありがとうございましたー。

反省

  • スライドが大盛りになって時間がかなりカツカツになってしまった…
    • かなり削ったけどもうちょい削れる事ができそうだったなあ…
    • 結果的には時間ちょうどで終わらせる事ができたけどゆっくり喋れなくて翻訳の方に申し訳ない…
  • Rensei が対応している Ruby のバージョンを表記しておくべきだった
    • Ruby 2.6 ~ 3.1dev まで対応している
  • スライドの作成がギリギリになっていた
    • 練習するたびにスライドのミスを見つけてつらい
    • 最後の1週間はずーっとスライドの練習に付き合ってもらっていた。めっちゃ感謝
  • 実際の配信画面を見てなかったので実際にどう映っていたのかなんもわからねえ

RubyKaigi で気になった発表

  • TypeProf for IDE: Enrich Dev-Experience without Annotations
    • Ruby の TypeProf を利用して静的片付けのような開発体験を実現する話
    • 静的片付けがほしいのではなくて開発体験がほしいわかる
      • 静的片付けがほしいのではなくて開発体験をよくしたい〜〜〜
    • シグネチャ表示がほしい…ほしい…
    • VSCode 高機能なので Vim でもほしい
    • 静的コードチェックは Rubocop 使っているけどもうちょい別の Lint を使いたい
      • 言語的な意味でのエラーチェックを行ってほしい
    • gem のコード開発は基本的に自身では呼ばれない事があるのでそのあたりどう TypeProf で解析するようになるのかは気になる
      • テストコードを含めて TypeProf で解析するようにしたい
    • RBS ファイルをシュッと記述する機能は Vim にもほしい
      • こういうのは TypeProf 側で提供されている機能になるんかな
      • なるべく VSCode に依存しないような機能として提供されてほしい
    • プロジェクトごとに TypeProf の LSP を起動するのはどうするのがいいんじゃろうか
  • The Art of Execution Control for Ruby's Debugger
    • Ruby のデバッガの debug-gem の話
    • コンソール画面で入力コマンドが見えなくて sob
    • VSCode で変数の中身が見れるの便利だなあ
      • Vim でもやりたい
    • break do: 便利そう
    • 今すぐ欲しいリモートデバッグ
    • 高機能なので使いこなせるかが不安
      • 使いこなしたい
    • step back すごい
    • デバッグだけ VSCode を使うのでもいい気がしてきた
    • TracePoint を使って実装されている、と
    • エディタとの連携部分がもうちょい知りたかった
  • Parallel testing with Ractors: putting CPUs to work
    • Ractor を使った並列テストを行うためのテストフレームワークの話
    • どう並列テストを行うかの解説がわかりやすい
    • Ractor をある程度理解してないとコードをその場で追うのは大変かも
    • テストフレームワークの実装や処理の流れが理解しやすかった
    • デモのテスト実行中にいろいろと操作できるのがすごい
    • Ractor の制限とかバグ無限につらそう
  • Demystifying DSLs for better analysis and understanding
    • RubyDSL を静的解析の話
    • Ruby のコードを例にして DLS を解説しているので『DSL is なに』みたいな人が見てみると理解が捗りそう
      • メタプロの話もでてくるのでそのあたりも学習につながる
    • DSL から RBI を生成し、 RBI で静的チェックなどを行う
    • RubyVim の親和性をもっとよくしたい
  • Parsing Ruby
    • Ruby の歴史を追っていく話
    • 知らない話がでてきて面白い
    • 今までいろんなことをやってきたんだなあ
      • parser-gem は 2.0 の時代につくられたのか
  • include/prepend in refinements should be prohibited
    • Refinements つらい話
    • 話の中であったハマりポイントは実際自分も何度かハマっているので代替機能はほしいんだが〜〜〜
    • include / prepend は多用しているので将来的に deprecated になるのは怖いがある程度しょうがなさそう
    • 代替機能があるならまあがんばって移行していくしかないなあ
  • Beware the Dead End!!
    • end がなかった時などのシンタックスエラーがどこでエラーになっているのかを検知する gem の話
    • 内容がコミカルで面白い
    • gem-dead_end 今すぐ使いたい!!
  • Graphical Terminal User Interface of Ruby 3.1
    • 今年も沢登りの話、と思いきや reline にダイアログ機能が実装されて irb で自動補完が追加された話
    • 風景はあいかわらずだけど内容はガチ
    • 実際に最新の irb を使うとかなりインパクトがでかい
  • Dive into Encoding