Dissecting a Ruby Block

Dissecting a Ruby Block

A deep dive into how MRI implements blocks, Ruby's implementation of closures. Also an explanation of the difference between blocks, lambdas, procs and bindings.

Eb5382b137146b381dd13740d165d1f2?s=128

Pat Shaughnessy

November 01, 2012
Tweet

Transcript

  1. Dissecting a Ruby Block Pat Shaughnessy http://patshaughnessy.net @pat_shaughnessy RubyConf Nov.

    1, 2012
  2. Why learn Ruby internals?

  3. 10.times do |n| puts n end

  4. None
  5. Blocks: Closures in Ruby Closures and Metaprogramming

  6. rb_block_t ??

  7. Calling a block

  8. 10.times do str = "The quick brown fox..." puts str

    end
  9. rb_block_t iseq putstring "The quick brown fox… " setdynamic str,

    0 putself getdynamic str, 0 send :puts, 1 leave
  10. Referencing variables from the parent scope

  11. str = "The quick brown fox" 10.times do str2 =

    "jumps over the lazy dog." puts "#{str} #{str2}" end
  12. str = "The quick brown fox"

  13. YARV internal stack locals: str rb_control_frame_t DFP Stack frame for

    top level scope
  14. str = "The quick brown fox" 10.times do ...

  15. YARV internal stack locals: str rb_block_t iseq DFP rb_control_frame_t DFP

    Stack frame for top level scope
  16. YARV internal stack locals: str rb_control_frame_t Stack frame for internal

    Fixnum.times C code
  17. ... do str2 = "jumps over the lazy dog." puts

    "#{str} #{str2}" end
  18. rb_block_t iseq DFP YARV internal stack locals: str locals: str2

    DFP rb_control_frame_t Stack frame for block code
  19. rb_block_t iseq DFP putstring "jumps over the lazy dog." setdynamic

    str, 0 putself getdynamic str2, 1 tostring putstring " " getdynamic str, 0 tostring concatstrings 3 send :puts, 1, nil, 8, <ic:0> leave YARV internal stack locals: str
  20. None
  21. In order to solve this problem we introduce the notion

    of a closure [11, 14] which is a data structure containing a lambda expression, and an environment to be used when that lambda expression is applied to arguments. Sussman and Steele. Scheme: An interpreter for extended lambda calculus
  22. Creating and calling a lambda

  23. None
  24. (lambda (arg) (/ arg 2))

  25. def display_message str = "The quick brown fox" lambda do

    str2 = "jumps over the lazy dog" puts "#{str} #{str2}" end end display_message.call
  26. def display_message str = "The quick brown fox" ... end

  27. YARV internal stack locals: str rb_control_frame_t Stack frame for message_function

  28. str = "The quick brown fox" lambda do ... end

  29. YARV internal stack rb_env_t env rb_proc_t rb_block_t iseq DFP envval

    is_lambda Stack Heap str locals: str rb_control_frame_t message_function stack frame
  30. display_message.call

  31. def display_message str = "The quick brown fox" lambda do

    str2 = "jumps over the lazy dog" puts "#{str} #{str2}" end end display_message.call
  32. YARV internal stack rb_control_frame_t stack frame for Proc.call Heap Stack

    str2 DFP rb_env_t env rb_proc_t rb_block_t iseq DFP envval is_lambda str
  33. Blocks: Closures in Ruby Closures and Metaprogramming

  34. Using a closure to define a method

  35. class Quote def initialize @str = "The quick brown fox..."

    end def display_message puts @str end end
  36. class Quote def initialize @str = "The quick brown fox..."

    end define_method :display_message do puts @str end end
  37. class Quote def initialize @str = "The quick brown fox"

    end end str2 = "jumps over the lazy dog." Quote.send(:define_method, :display_message) do puts "#{@str} #{str2}" end
  38. eval and binding

  39. str = "puts" str += " 2" str += "

    +" str += " 2" eval(str) => 4
  40. class Quote def initialize @str = "The quick brown fox..."

    end def get_binding binding end end
  41. obj = Quote.new

  42. rb_control_frame_t self DFP, LFP, SP YARV internal stack PC, etc.

    RObject ivptr klass @str etc...
  43. obj = Quote.new eval('puts @str', obj.get_binding)

  44. rb_binding_t line_no env Stack Heap rb_control_frame_t get_binding stack frame filename

    rb_env_t rb_block_t iseq DFP RObject ivptr klass @str self, DFP self
  45. putstring "The quick brown fox… " setdynamic str, 0 putself

    getdynamic str, 0 send :puts, 1 leave YARV internal stack locals: str RObject ivptr klass rb_block_t DFP self iseq
  46. None