Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Use Macro all the time ~ マクロを使いまくろ ~ 感想戦
Search
osyo
September 15, 2021
Programming
0
290
Use Macro all the time ~ マクロを使いまくろ ~ 感想戦
Fukuoka.rb #226 - RubyKaigi 感想戦
osyo
September 15, 2021
Tweet
Share
More Decks by osyo
See All by osyo
5分で話せる Ruby 3.1
osyo
0
160
AST を使って ActiveRecord の where の条件式をブロックで記述しよう
osyo
2
1.2k
Vim の開発環境自慢
osyo
5
3k
Use Macro all the time ~ マクロを使いまくろ ~ (English)
osyo
3
380
Use Macro all the time ~ マクロを使いまくろ ~ (日本語)
osyo
0
2.2k
月単位でイテレーションする
osyo
0
320
Ruby 3.0 で変わった private と attr_xxx
osyo
1
720
Ruby 2.0 から Ruby 3.0 を駆け足で振り返る
osyo
0
1.8k
12月25日にリリースされる Ruby 3.0 に備えよう!
osyo
1
3.1k
Other Decks in Programming
See All in Programming
技術選定を未来に繋いで活用していく
sakito
3
110
MCP調べてみました! / Exploring MCP
uhzz
2
2.2k
リストビュー画面UX改善の振り返り
splcywolf
0
130
Agentic Applications with Symfony
el_stoffel
2
270
メモリウォールを超えて:キャッシュメモリ技術の進歩
kawayu
0
1.9k
新しいPHP拡張モジュールインストール方法「PHP Installer for Extensions (PIE)」を使ってみよう!
cocoeyes02
0
360
これだけは知っておきたいクラス設計の基礎知識 version 2
masuda220
PRO
24
6.2k
Unlock the Potential of Swift Code Generation
rockname
0
240
ミリしらMCP勉強会
watany
4
740
PHP で学ぶ OAuth 入門
azuki
1
150
SwiftUI API Design Lessons
niw
1
270
AIコーディングワークフローの試行 〜AIエージェント×ワークフローでの自動化を目指して〜
rkaga
2
3.5k
Featured
See All Featured
Unsuck your backbone
ammeep
670
57k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
52
2.4k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
178
53k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
34
2.2k
How GitHub (no longer) Works
holman
314
140k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.4k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Practical Orchestrator
shlominoach
186
10k
Making the Leap to Tech Lead
cromwellryan
133
9.2k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.2k
Code Review Best Practice
trishagee
67
18k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
Transcript
Use Macro all the time ~ マクロを使いまくろ ~ 感想戦 Fukuoka.rb
#226 - RubyKaigi 感想戦
自己紹介 osyo @pink_bangbi: https://twitter.com/pink_bangbi osyo-manga: https://github.com/osyo-manga Secret Garden(Instrumental): http://secret-garden.hatenablog.com Rails
エンジニア 好きな Ruby の機能は Refinements RubyKaigi は初参加 Use Macro all the time ~ マクロを使いまくろ ~ https://speakerdeck.com/osyo/use-macro-all-the-time-makurowoshi-imakuro-ri-ben-yu
RubyKaigi の感想戦と補足
元々のモチベーション
元々のモチベーション 元々はブロックから Ruby のコードを取得したい要求があった 最初は iseq から位置情報を取得してファイルから読み込んできてた ただ、これだとパフォーマンス的な懸念点があった
元々のモチベーション 元々はブロックから Ruby のコードを取得したい要求があった 最初は iseq から位置情報を取得してファイルから読み込んできてた ただ、これだとパフォーマンス的な懸念点があった RubyVM::AST だとブロックから
AST を取得できる実装を書いた ` `
元々のモチベーション 元々はブロックから Ruby のコードを取得したい要求があった 最初は iseq から位置情報を取得してファイルから読み込んできてた ただ、これだとパフォーマンス的な懸念点があった RubyVM::AST だとブロックから
AST を取得できる実装を書いた これを利用するとマクロができるのでは…? この構想自体は1年ぐらい前からあった ` `
元々のモチベーション 元々はブロックから Ruby のコードを取得したい要求があった 最初は iseq から位置情報を取得してファイルから読み込んできてた ただ、これだとパフォーマンス的な懸念点があった RubyVM::AST だとブロックから
AST を取得できる実装を書いた これを利用するとマクロができるのでは…? この構想自体は1年ぐらい前からあった 知り合いに釣られて RubyKaigi に参加するモチベーションが湧いたので今回マクロを実 装した RubyKaigi 駆動開発 ` `
Rensei の作業期間・苦労話
Rensei の作業期間・苦労話 作業期間は 3〜4ヶ月 実際作業してたのは去年の今頃
Rensei の作業期間・苦労話 作業期間は 3〜4ヶ月 実際作業してたのは去年の今頃 Rensei は作業量とバグ修正が無限にあってつらかった 100種類以上の AST を1つずつ実装していた
Rensei の作業期間・苦労話 作業期間は 3〜4ヶ月 実際作業してたのは去年の今頃 Rensei は作業量とバグ修正が無限にあってつらかった 100種類以上の AST を1つずつ実装していた
1 proc { |z, (a, b), c = 1, d = 2, *, (e, f, g), (h, i), j, k, l:, **kwd| foo }
Rensei の作業期間・苦労話 作業期間は 3〜4ヶ月 実際作業してたのは去年の今頃 Rensei は作業量とバグ修正が無限にあってつらかった 100種類以上の AST を1つずつ実装していた
1 proc { |z, (a, b), c = 1, d = 2, *, (e, f, g), (h, i), j, k, l:, **kwd| foo } 実装した後も ActiveRecord のソースファイルを1つずつ食わせていくとバグが無限に発 生して1つずつ直していった エッジケースの問題が大量にあった…
Rensei の作業期間・苦労話 作業期間は 3〜4ヶ月 実際作業してたのは去年の今頃 Rensei は作業量とバグ修正が無限にあってつらかった 100種類以上の AST を1つずつ実装していた
1 proc { |z, (a, b), c = 1, d = 2, *, (e, f, g), (h, i), j, k, l:, **kwd| foo } 実装した後も ActiveRecord のソースファイルを1つずつ食わせていくとバグが無限に発 生して1つずつ直していった エッジケースの問題が大量にあった… テストはかなり力を入れて書いた AST から復元したコードが元の AST と同じかどうかでテストしてる https://github.com/osyo-manga/gem- rensei/blob/82aaf139935a8b4eb7fd1029cdc5fc86e4fb692a/spec/unparser_spec.rb
Rensei の実装に関して 当日は時間がなかったので割愛したが以下のような感じで実装してる 1 def unparse(node) 2 case node&.type 3
when :SCOPE 4 unparse(node.children.last) 5 when :LIT 6 "#{node.children.last}" 7 when :STR 8 "#{node.children.last}" 9 when :CALL 10 recv, meth = node.children 11 "#{unparse(recv)}.#{meth}" 12 when :OPCALL 13 left, op, args = node.children 14 "(#{unparse(left)} #{op} #{unparse(args.children[0])})" 15 end 16 end 17 node = RubyVM::AbstractSyntaxTree.parse("1 + 2 * '42'.to_i") 18 pp unparse(node) # => "(1 + (2 * 42.to_i))"
Kenma の作業期間・苦労話
Kenma の作業期間・苦労話 作業期間 1〜2ヶ月 今回の RubyKaigi に向けて実装した
Kenma の作業期間・苦労話 作業期間 1〜2ヶ月 今回の RubyKaigi に向けて実装した 実装自体はそこまで難しくなかったが、とにかく書き心地を重視して API を設計した
何回も実装を書き直しながら方向性が確定してから gem をつくりはじめた いろんな人に壁打ちしながらつくってた感謝
Kenma の作業期間・苦労話 作業期間 1〜2ヶ月 今回の RubyKaigi に向けて実装した 実装自体はそこまで難しくなかったが、とにかく書き心地を重視して API を設計した
何回も実装を書き直しながら方向性が確定してから gem をつくりはじめた いろんな人に壁打ちしながらつくってた感謝 マクロのデザインに関してはかなり Rust を意識した マクロ関数に ! 付けたりとか ` `
Kenma の作業期間・苦労話 作業期間 1〜2ヶ月 今回の RubyKaigi に向けて実装した 実装自体はそこまで難しくなかったが、とにかく書き心地を重視して API を設計した
何回も実装を書き直しながら方向性が確定してから gem をつくりはじめた いろんな人に壁打ちしながらつくってた感謝 マクロのデザインに関してはかなり Rust を意識した マクロ関数に ! 付けたりとか 結果的に定義方法を種類分けしつつ、抽象的なマクロの定義ができて個人的には満足 パターンマクロがかなり抽象的にかけてよい ` `
RubyVM::AST の互換性 ` `
RubyVM::AST の互換性 Ruby のバージョン間で互換性が保証されてない つらかったのは Ruby 2.6 -> 2.7 で
:ARRAY -> :LIST にタイプ名が変わったところ ` ` ` `
RubyVM::AST の互換性 Ruby のバージョン間で互換性が保証されてない つらかったのは Ruby 2.6 -> 2.7 で
:ARRAY -> :LIST にタイプ名が変わったところ 1 pp RUBY_VERSION # => "2.6.8" 2 pp RubyVM::AbstractSyntaxTree.parse("[1, 2, 3]").children.last 3 # => (ARRAY@1:0-1:9 (LIT@1:1-1:2 1) (LIT@1:4-1:5 2) (LIT@1:7-1:8 3) nil) ` ` ` `
RubyVM::AST の互換性 Ruby のバージョン間で互換性が保証されてない つらかったのは Ruby 2.6 -> 2.7 で
:ARRAY -> :LIST にタイプ名が変わったところ 1 pp RUBY_VERSION # => "2.6.8" 2 pp RubyVM::AbstractSyntaxTree.parse("[1, 2, 3]").children.last 3 # => (ARRAY@1:0-1:9 (LIT@1:1-1:2 1) (LIT@1:4-1:5 2) (LIT@1:7-1:8 3) nil) 1 pp RUBY_VERSION # => "2.7.4" 2 pp RubyVM::AbstractSyntaxTree.parse("[1, 2, 3]").children.last 3 # => (LIST@1:0-1:9 (LIT@1:1-1:2 1) (LIT@1:4-1:5 2) (LIT@1:7-1:8 3) nil) ` ` ` `
RubyVM::AST の互換性 Ruby のバージョン間で互換性が保証されてない つらかったのは Ruby 2.6 -> 2.7 で
:ARRAY -> :LIST にタイプ名が変わったところ 1 pp RUBY_VERSION # => "2.6.8" 2 pp RubyVM::AbstractSyntaxTree.parse("[1, 2, 3]").children.last 3 # => (ARRAY@1:0-1:9 (LIT@1:1-1:2 1) (LIT@1:4-1:5 2) (LIT@1:7-1:8 3) nil) 1 pp RUBY_VERSION # => "2.7.4" 2 pp RubyVM::AbstractSyntaxTree.parse("[1, 2, 3]").children.last 3 # => (LIST@1:0-1:9 (LIT@1:1-1:2 1) (LIT@1:4-1:5 2) (LIT@1:7-1:8 3) nil) どっちかって言うと細かいところでバグってるところのほうがつらかった Ruby の構文としては意味が異なるのに AST としては同じになるとか… proc { |a| } と proc { |a,| } が同じ AST になる とか https://bugs.ruby-lang.org/issues/17015 ` ` ` ` ` ` ` `
Rensei の AST 間のバージョン対応
Rensei の AST 間のバージョン対応 Rensei は現時点で存在してるバージョンはすべて対応している Ruby 2.6 ~ 3.1-dev
Rensei の AST 間のバージョン対応 Rensei は現時点で存在してるバージョンはすべて対応している Ruby 2.6 ~ 3.1-dev
バージョン間で細かい非互換はあるけど基本的には新しい構文を追加するような実装に なっている
Rensei の AST 間のバージョン対応 Rensei は現時点で存在してるバージョンはすべて対応している Ruby 2.6 ~ 3.1-dev
バージョン間で細かい非互換はあるけど基本的には新しい構文を追加するような実装に なっている 詳しくは実装を見てね!! https://github.com/osyo-manga/gem- rensei/blob/82aaf139935a8b4eb7fd1029cdc5fc86e4fb692a/lib/rensei/unparser.rb
マクロの今後
マクロの今後 エンドユーザがマクロを使うと言うよりかは間接的にマクロが利用できるようにしたい ファイル全体ではなくて局所的にマクロを利用したい ユーザが定義したブロックでのみ使用するなど
マクロの今後 エンドユーザがマクロを使うと言うよりかは間接的にマクロが利用できるようにしたい ファイル全体ではなくて局所的にマクロを利用したい ユーザが定義したブロックでのみ使用するなど 例えばこんな感じ
マクロの今後 エンドユーザがマクロを使うと言うよりかは間接的にマクロが利用できるようにしたい ファイル全体ではなくて局所的にマクロを利用したい ユーザが定義したブロックでのみ使用するなど 例えばこんな感じ 1 User.where { :age <
20 }
マクロの今後 エンドユーザがマクロを使うと言うよりかは間接的にマクロが利用できるようにしたい ファイル全体ではなくて局所的にマクロを利用したい ユーザが定義したブロックでのみ使用するなど 例えばこんな感じ 1 User.where { :age <
20 } 1 User.where("age < 20")
マクロの今後 エンドユーザがマクロを使うと言うよりかは間接的にマクロが利用できるようにしたい ファイル全体ではなくて局所的にマクロを利用したい ユーザが定義したブロックでのみ使用するなど 例えばこんな感じ 1 User.where { :age <
20 } 1 User.where("age < 20") マクロと言っているがどちらかというと AST というデータに対して今後フォーカスを当 てて行くような未来が見えてきた気がする マクロでないにしても今後 AST を使って便利ななにかができてきそう
おまけ
おまけ Ruby 3.1 で RubyVM::AST::Node から元のコードが取得できるようになる(かも) ` `
おまけ Ruby 3.1 で RubyVM::AST::Node から元のコードが取得できるようになる(かも) 1 src = <<~EOS
2 if hoge 3 puts hoge + foo 4 end 5 EOS 6 7 # keep_script_lines を true にすると 8 node = RubyVM::AbstractSyntaxTree.parse(src, keep_script_lines: true) 9 10 # #source メソッドでコードを取得できるようになる 11 puts node.source 12 # => if hoge 13 # puts hoge + foo 14 # end ` `