SlideShare a Scribd company logo
Invitation
to
the Dark Side of Ruby
RubyKaigi 2019 LT
Satoshi Tagomori (@tagomoris)
Satoshi Tagomori (@tagomoris)
Fluentd, MessagePack-Ruby, Norikra, Woothee, ...
Arm Ltd.
Invitation to the dark side of Ruby
• Treasure Data acquired by Arm at 2018
• We're hiring!

at Mountain View, Tokyo, Vancouver and Cambridge
• RubyKaigi 2019 After Party Sponsor!
Invitation
to
the Dark Side of Ruby
"If you only knew the power of the dark side"
<<暗黒面のパワーはすばらしいぞ>>
EXAMPLE: 1 < 2 < 3
1 < 2 < 3
true < 3
undefined method `<' for true:TrueClass
EXAMPLE: 1 < 2 < 3
RUBY: 1 < 2 && 2 < 3
Can we do this routine
conversion automatically?
More ActiveRecord Examples (1)
More ActiveRecord Examples (2)
More ActiveRecord Examples (3)
?
MACRO
I NEVER introduce macro to Ruby.
MY NEW PRODUCT:
MACRO
POST PROCESSOR
MY-MACRO: REGISTER
• Register rules with "before" and "after" matchers
• "before"/"after": code in Ruby, with DSL for code placeholders
• Placeholders (N: integer, >= 1):
• eN: expressions which returns a value or values
• vN: a single value (e.g., variable, constant, literal, etc)
MY-MACRO: APPLY ON METHODS
• Macro rules applied to methods
• manually: apply(ModName, ModName.method(:method_name)
• automatically: using TracePoint (:end)
• Registered matchers:
• replace placeholders w/ matcher nodes for RubyVM::AST::Node
MY-MACRO: PREPARE(2)
VCALL: e1 VCALL: e2
VCALL: e3
Matcher: e1 Matcher: e2
Matcher: e3
OPCALL: <
OPCALL: <
OPCALL: <
OPCALL: <
Matcher code
e1 < e2 < e3
Placeholders
Matcher tree
• Applied methods:
• parsed into AST using RubyVM::AbstractSyntaxTree#of
• get the source of the method
#<UnboundMethod: Mod#foo>
def foo(v)
if bar(v) < 1
# ...
elsif bar(v) == 1
# ...
elsif 1 < bar(v) < 2
# ...
else
# ...
end
end
MY-MACRO: MATCH TO METHOD
def foo(v)
if bar(v) < 1
# ...
elsif bar(v) == 1
# ...
elsif 1 < bar(v) < 2
# ...
else
# ...
end
end
• Matched tree:
• get code snippets for matcher nodes
• e1: 1 (literal)
• e2: bar(v) (func call with v)
• e3: 2 (literal)
e1 < e2 < e3
Matcher: e1 Matcher: e2
Matcher: e3
MY-MACRO: REWRITE CODE (1)
• Matched tree:
• replace patterns in "after" code with captured snippets
• e1: 1 (literal)
• e2: bar(v)
• e3: 2 (literal)
e1 < e2 && e2 < e3
"After" code
1 < bar(v) && bar(v) < 2
"After" code for this method
"Before" code
e1 < e2 < e3
MY-MACRO: REWRITE CODE (2)
• Matched tree:
• rewrite original method code

with updated "after" code
def foo(v)
if bar(v) < 1
# ...
elsif bar(v) == 1
# ...
elsif 1 < bar(v) < 2
# ...
else
# ...
end
end
e1 < e2 && e2 < e3
"After" code
1 < bar(v) && bar(v) < 2
"After" code for this method
"Before" code
e1 < e2 < e3
Original code of this method
MY-MACRO: REWRITE CODE (3)
• Matched tree:
• rewrite original method code

with updated "after" code
1 < bar(v) && bar(v) < 2
"After" code for this method
def foo(v)
if bar(v) < 1
# ...
elsif bar(v) == 1
# ...
elsif 1 < bar(v) && bar(v) < 2
# ...
else
# ...
end
end
Updated code of this method
e1 < e2 && e2 < e3
"After" code
"Before" code
e1 < e2 < e3
MY-MACRO: REWRITE CODE (3)
• Rewritten method source:
• call Modue#module_eval to replace the method definition
def foo(v)
if bar(v) < 1
# ...
elsif bar(v) == 1
# ...
elsif 1 < bar(v) < 2
# ...
else
# ...
end
end
def foo(v)
if bar(v) < 1
# ...
elsif bar(v) == 1
# ...
elsif 1 < bar(v) && bar(v) < 2
# ...
else
# ...
end
end
Overwrite the method definition
by Module#module_eval
MY-MACRO: REWRITE METHOD
NICE STUFFS
• Less performance penalty
• Macro processor works only when methods are defined
• Ruby version independent Macro rules
• Compatible between Ruby versions
• Because of rules written in Ruby code
• Built-in rules, useful for some cases
• Continuous inequality operators (<=, <, >, >=)
• ActiveRecord utilities
LIMITATIONS
• Supported only on Ruby 2.6 or later
• Only code in def in module/class are rewritable
• Calling class method just after definition can't be updated
• Same method can't be rewritten twice
• Non-idempotent methods causes unexpected result
• Updating Lambda and local variable name matching is not implemented yet
• Stack trace of updated methods would be confusing
• Source from STDIN or command line (and irb/pry) cause errors
• Rule-order-dependent issues will be caused
• Method visibility are not taken care right now
• Non-self singleton method definition causes errors
• Placeholder validations are not implemented yet
• Supported only on Ruby 2.6 or later
• Only code in def in module/class are rewritable
• Calling class method just after definition can't be updated
• Same method can't be rewritten twice
• Non-idempotent methods causes unexpected result
• Updating Lambda and local variable name matching is not implemented yet
• Stack trace of updated methods would be confusing
• Source from STDIN or command line (and irb/pry) cause errors
• Rule-order-dependent issues will be caused
• Method visibility are not taken care right now
• Non-self singleton method definition causes errors
• Placeholder validations are not implemented yet
LIMITATIONS
Just Few
Limitations
NAMING?
MACRO,
BLACK MAGICS,
...
Maccro
≒真っ黒 (pure black)
DONEC QUIS NUNC
Thank you!
Join us!
https://github.com/tagomoris/maccro
https://rubygems.org/gems/maccro
Let's play with macro together!
"Join me and I will complete your training."
<<仲間になれ。修行を完成させてやる>>
Thank you!

More Related Content

Invitation to the dark side of Ruby

  • 1. Invitation to the Dark Side of Ruby RubyKaigi 2019 LT Satoshi Tagomori (@tagomoris)
  • 2. Satoshi Tagomori (@tagomoris) Fluentd, MessagePack-Ruby, Norikra, Woothee, ... Arm Ltd.
  • 4. • Treasure Data acquired by Arm at 2018 • We're hiring!
 at Mountain View, Tokyo, Vancouver and Cambridge • RubyKaigi 2019 After Party Sponsor!
  • 6. "If you only knew the power of the dark side" <<暗黒面のパワーはすばらしいぞ>>
  • 7. EXAMPLE: 1 < 2 < 3 1 < 2 < 3 true < 3 undefined method `<' for true:TrueClass
  • 8. EXAMPLE: 1 < 2 < 3 RUBY: 1 < 2 && 2 < 3 Can we do this routine conversion automatically?
  • 12. ?
  • 13. MACRO
  • 14. I NEVER introduce macro to Ruby.
  • 16. MY-MACRO: REGISTER • Register rules with "before" and "after" matchers • "before"/"after": code in Ruby, with DSL for code placeholders • Placeholders (N: integer, >= 1): • eN: expressions which returns a value or values • vN: a single value (e.g., variable, constant, literal, etc)
  • 17. MY-MACRO: APPLY ON METHODS • Macro rules applied to methods • manually: apply(ModName, ModName.method(:method_name) • automatically: using TracePoint (:end)
  • 18. • Registered matchers: • replace placeholders w/ matcher nodes for RubyVM::AST::Node MY-MACRO: PREPARE(2) VCALL: e1 VCALL: e2 VCALL: e3 Matcher: e1 Matcher: e2 Matcher: e3 OPCALL: < OPCALL: < OPCALL: < OPCALL: < Matcher code e1 < e2 < e3 Placeholders Matcher tree
  • 19. • Applied methods: • parsed into AST using RubyVM::AbstractSyntaxTree#of • get the source of the method #<UnboundMethod: Mod#foo> def foo(v) if bar(v) < 1 # ... elsif bar(v) == 1 # ... elsif 1 < bar(v) < 2 # ... else # ... end end MY-MACRO: MATCH TO METHOD
  • 20. def foo(v) if bar(v) < 1 # ... elsif bar(v) == 1 # ... elsif 1 < bar(v) < 2 # ... else # ... end end • Matched tree: • get code snippets for matcher nodes • e1: 1 (literal) • e2: bar(v) (func call with v) • e3: 2 (literal) e1 < e2 < e3 Matcher: e1 Matcher: e2 Matcher: e3 MY-MACRO: REWRITE CODE (1)
  • 21. • Matched tree: • replace patterns in "after" code with captured snippets • e1: 1 (literal) • e2: bar(v) • e3: 2 (literal) e1 < e2 && e2 < e3 "After" code 1 < bar(v) && bar(v) < 2 "After" code for this method "Before" code e1 < e2 < e3 MY-MACRO: REWRITE CODE (2)
  • 22. • Matched tree: • rewrite original method code
 with updated "after" code def foo(v) if bar(v) < 1 # ... elsif bar(v) == 1 # ... elsif 1 < bar(v) < 2 # ... else # ... end end e1 < e2 && e2 < e3 "After" code 1 < bar(v) && bar(v) < 2 "After" code for this method "Before" code e1 < e2 < e3 Original code of this method MY-MACRO: REWRITE CODE (3)
  • 23. • Matched tree: • rewrite original method code
 with updated "after" code 1 < bar(v) && bar(v) < 2 "After" code for this method def foo(v) if bar(v) < 1 # ... elsif bar(v) == 1 # ... elsif 1 < bar(v) && bar(v) < 2 # ... else # ... end end Updated code of this method e1 < e2 && e2 < e3 "After" code "Before" code e1 < e2 < e3 MY-MACRO: REWRITE CODE (3)
  • 24. • Rewritten method source: • call Modue#module_eval to replace the method definition def foo(v) if bar(v) < 1 # ... elsif bar(v) == 1 # ... elsif 1 < bar(v) < 2 # ... else # ... end end def foo(v) if bar(v) < 1 # ... elsif bar(v) == 1 # ... elsif 1 < bar(v) && bar(v) < 2 # ... else # ... end end Overwrite the method definition by Module#module_eval MY-MACRO: REWRITE METHOD
  • 25. NICE STUFFS • Less performance penalty • Macro processor works only when methods are defined • Ruby version independent Macro rules • Compatible between Ruby versions • Because of rules written in Ruby code • Built-in rules, useful for some cases • Continuous inequality operators (<=, <, >, >=) • ActiveRecord utilities
  • 26. LIMITATIONS • Supported only on Ruby 2.6 or later • Only code in def in module/class are rewritable • Calling class method just after definition can't be updated • Same method can't be rewritten twice • Non-idempotent methods causes unexpected result • Updating Lambda and local variable name matching is not implemented yet • Stack trace of updated methods would be confusing • Source from STDIN or command line (and irb/pry) cause errors • Rule-order-dependent issues will be caused • Method visibility are not taken care right now • Non-self singleton method definition causes errors • Placeholder validations are not implemented yet
  • 27. • Supported only on Ruby 2.6 or later • Only code in def in module/class are rewritable • Calling class method just after definition can't be updated • Same method can't be rewritten twice • Non-idempotent methods causes unexpected result • Updating Lambda and local variable name matching is not implemented yet • Stack trace of updated methods would be confusing • Source from STDIN or command line (and irb/pry) cause errors • Rule-order-dependent issues will be caused • Method visibility are not taken care right now • Non-self singleton method definition causes errors • Placeholder validations are not implemented yet LIMITATIONS Just Few Limitations
  • 30. DONEC QUIS NUNC Thank you! Join us! https://github.com/tagomoris/maccro https://rubygems.org/gems/maccro Let's play with macro together!
  • 31. "Join me and I will complete your training." <<仲間になれ。修行を完成させてやる>> Thank you!