Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Blocks, procs and lambdas

Blocks, procs and lambdas

An introduction to blocks in Ruby. It stars by looking at the very basics of how to pass and run blocks, arguments, return values and the Proc class. It ends with some more advanced usage of blocks, and some weird tricks that you'll probably never use.

Jonathan Cairns

March 21, 2013
Tweet

Other Decks in Technology

Transcript

  1. What are Ruby blocks? • A block in Ruby is

    a closure in computer science language – it's a block of code
  2. What are Ruby blocks? • A block in Ruby is

    a closure in computer science language – it's a block of code • Ruby is not designed as a primarily functional programming language
  3. What are Ruby blocks? • A block in Ruby is

    a closure in computer science language – it's a block of code • Ruby is not designed as a primarily functional programming language • Ruby methods are not objects, they can't be passed around as variables – we use blocks instead
  4. What are Ruby blocks? • A block in Ruby is

    a closure in computer science language – it's a block of code • Ruby is not designed as a primarily functional programming language • Ruby methods are not objects, they can't be passed around as variables – we use blocks instead my_method = def say_hello puts "hello" end puts my_method.inspect my_method = def say_hello puts "hello" end puts my_method.inspect
  5. What are Ruby blocks? my_method = def say_hello puts "hello"

    end puts my_method.inspect my_method = def say_hello puts "hello" end puts my_method.inspect • A block in Ruby is a closure in computer science language – it's a block of code • Ruby is not designed as a primarily functional programming language • Ruby methods are not objects, they can't be passed around as variables – we use blocks instead #=> nil #=> nil
  6. What are Ruby blocks? Used for: • Iterating over arrays

    and other enumerators [1, 2, 3].each do |i| puts i end [1, 2, 3].each do |i| puts i end
  7. What are Ruby blocks? Used for: • Iterating over arrays

    and other enumerators • Storing code as a variable code = Proc.new { puts “hello” } code = Proc.new { puts “hello” } [1, 2, 3].each do |i| puts i end [1, 2, 3].each do |i| puts i end
  8. What are Ruby blocks? Used for: • Iterating over arrays

    and other enumerators • Storing code as a variable • Callbacks code = Proc.new { puts “hello” } code = Proc.new { puts “hello” } HTTP.get(uri) do |response| puts response.headers end HTTP.get(uri) do |response| puts response.headers end [1, 2, 3].each do |i| puts i end [1, 2, 3].each do |i| puts i end
  9. Block syntax [1, 2, 3].each { |i| puts i }

    [1, 2, 3].each { |i| puts i } Braces:
  10. Block syntax [1, 2, 3].each { |i| puts i }

    [1, 2, 3].each { |i| puts i } [1, 2, 3].each do |i| puts i end [1, 2, 3].each do |i| puts i end Braces: do...end block:
  11. Block syntax [1, 2, 3].each { |i| puts i }

    [1, 2, 3].each { |i| puts i } [1, 2, 3].each do |i| puts i end [1, 2, 3].each do |i| puts i end Braces: do...end block: Functionally equivalent, but style conventions suggest only to use braces for single line blocks only
  12. More block examples [1, 2, 3, 4].collect! { |i| i

    ** 2 } [1, 2, 3, 4].collect! { |i| i ** 2 }
  13. More block examples [1, 2, 3, 4].collect! { |i| i

    ** 2 } [1, 2, 3, 4].collect! { |i| i ** 2 } #=> [1, 4, 9, 16] #=> [1, 4, 9, 16]
  14. More block examples clothes = {'long' => ‘johns’, 'hot' =>

    ‘pants’} clothes.each do |key, val| puts “I like #{key} #{val}” end clothes = {'long' => ‘johns’, 'hot' => ‘pants’} clothes.each do |key, val| puts “I like #{key} #{val}” end
  15. More block examples clothes = {'long' => ‘johns’, 'hot' =>

    ‘pants’} clothes.each do |key, val| puts “I like #{key} #{val}” end clothes = {'long' => ‘johns’, 'hot' => ‘pants’} clothes.each do |key, val| puts “I like #{key} #{val}” end #=> I like long johns #=> I like hot pants #=> I like long johns #=> I like hot pants
  16. Executing blocks A block gets passed to a method, but

    it isn’t run automatically – the method invokes it with yield
  17. Executing blocks A block gets passed to a method, but

    it isn’t run automatically – the method invokes it with yield def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” } def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” }
  18. Executing blocks A block gets passed to a method, but

    it isn’t run automatically – the method invokes it with yield def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” } def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” }
  19. Executing blocks A block gets passed to a method, but

    it isn’t run automatically – the method invokes it with yield def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” } def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” } #=> I’m about to run the block #=> Block block block #=> There, I told you #=> I’m about to run the block #=> Block block block #=> There, I told you
  20. Executing blocks A block gets passed to a method, but

    it isn’t run automatically – the method invokes it with yield def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” } def run_my_block puts “I’m about to run the block” yield puts “There, I told you” end run_my_block { puts “Block block block” } Note that the block doesn't appear in the function's parameter list
  21. Return values from blocks The return value of a block

    is also returned by yield, allowing you to do something with the return value in the calling method
  22. Return values from blocks The return value of a block

    is also returned by yield, allowing you to do something with the return value in the calling method def run_block_and_print_return puts “I’m about to run the block” block_return = yield puts “Block returned: #{block_return}” end run_block_and_print_return { “nothing interesting” } def run_block_and_print_return puts “I’m about to run the block” block_return = yield puts “Block returned: #{block_return}” end run_block_and_print_return { “nothing interesting” }
  23. Return values from blocks The return value of a block

    is also returned by yield, allowing you to do something with the return value in the calling method def run_block_and_print_return puts “I’m about to run the block” block_return = yield puts “Block returned: #{block_return}” end run_block_and_print_return { “nothing interesting” } def run_block_and_print_return puts “I’m about to run the block” block_return = yield puts “Block returned: #{block_return}” end run_block_and_print_return { “nothing interesting” }
  24. Return values from blocks The return value of a block

    is also returned by yield, allowing you to do something with the return value in the calling method def run_block_and_print_return puts “I’m about to run the block” block_return = yield puts “Block returned: #{block_return}” end run_block_and_print_return { “nothing interesting” } def run_block_and_print_return puts “I’m about to run the block” block_return = yield puts “Block returned: #{block_return}” end run_block_and_print_return { “nothing interesting” } #=> I’m about to run the block #=> Block returned: nothing interesting #=> I’m about to run the block #=> Block returned: nothing interesting
  25. Blocks with arguments Blocks can take arguments, which are passed

    as arguments to yield def give_me_random yield rand(1..10), rand(1..10) end # This block raises the first parameter # to the power of the second give_me_random do |a, b| puts “parameters: #{a}, #{b}” puts a ** b end def give_me_random yield rand(1..10), rand(1..10) end # This block raises the first parameter # to the power of the second give_me_random do |a, b| puts “parameters: #{a}, #{b}” puts a ** b end
  26. Blocks with arguments Blocks can take arguments, which are passed

    as arguments to yield def give_me_random yield rand(1..10), rand(1..10) end # This block raises the first parameter # to the power of the second give_me_random do |a, b| puts “parameters: #{a}, #{b}” puts a ** b end def give_me_random yield rand(1..10), rand(1..10) end # This block raises the first parameter # to the power of the second give_me_random do |a, b| puts “parameters: #{a}, #{b}” puts a ** b end
  27. Blocks with arguments Blocks can take arguments, which are passed

    as arguments to yield def give_me_random yield rand(1..10), rand(1..10) end # This block raises the first parameter # to the power of the second give_me_random do |a, b| puts “parameters: #{a}, #{b}” puts a ** b end def give_me_random yield rand(1..10), rand(1..10) end # This block raises the first parameter # to the power of the second give_me_random do |a, b| puts “parameters: #{a}, #{b}” puts a ** b end #=> parameters: 4, 2 #=> 16 #=> parameters: 4, 2 #=> 16
  28. Where's the block? What if yield is used when there's

    no block given? def i_need_a_block(n) yield n end i_need_a_block 3 def i_need_a_block(n) yield n end i_need_a_block 3
  29. Where's the block? What if yield is used when there's

    no block given? def i_need_a_block(n) yield n end i_need_a_block 3 def i_need_a_block(n) yield n end i_need_a_block 3 #=> no_block.rb:2:in `i_need_a_block': no block given (yield) (LocalJumpError) from no_block.rb:5:in `<main>' #=> no_block.rb:2:in `i_need_a_block': no block given (yield) (LocalJumpError) from no_block.rb:5:in `<main>'
  30. Block given? Use block_given? to check whether a block has

    been passed to the method def try_me if block_given? yield else puts "No block given" end end try_me try_me { puts "Block executed" } def try_me if block_given? yield else puts "No block given" end end try_me try_me { puts "Block executed" }
  31. Block given? Use block_given? to check whether a block has

    been passed to the method def try_me if block_given? yield else puts "No block given" end end try_me try_me { puts "Block executed" } def try_me if block_given? yield else puts "No block given" end end try_me try_me { puts "Block executed" }
  32. Block given? Use block_given? to check whether a block has

    been passed to the method def try_me if block_given? yield else puts "No block given" end end try_me try_me { puts "Block executed" } def try_me if block_given? yield else puts "No block given" end end try_me try_me { puts "Block executed" } #=> No block given #=> Block executed #=> No block given #=> Block executed
  33. Blocks and scope When a block is defined it remembers

    the current scope – you can access local variables even when executing the block in a different scope altogether
  34. Blocks and scope When a block is defined it remembers

    the current scope – you can access local variables even when executing the block in a different scope altogether def run_block puts “I can’t see x” unless defined? x yield end x = 10 y = 20 run_block do puts x + y end def run_block puts “I can’t see x” unless defined? x yield end x = 10 y = 20 run_block do puts x + y end
  35. Blocks and scope When a block is defined it remembers

    the current scope – you can access local variables even when executing the block in a different scope altogether def run_block puts “I can’t see x” unless defined? x yield end x = 10 y = 20 run_block do puts x + y end def run_block puts “I can’t see x” unless defined? x yield end x = 10 y = 20 run_block do puts x + y end
  36. Blocks and scope When a block is defined it remembers

    the current scope – you can access local variables even when executing the block in a different scope altogether def run_block puts “I can’t see x” unless defined? x yield end x = 10 y = 20 run_block do puts x + y end def run_block puts “I can’t see x” unless defined? x yield end x = 10 y = 20 run_block do puts x + y end #=> I can’t see x #=> 30 #=> I can’t see x #=> 30
  37. Blocks and scope Even if local variables with the same

    names are defined in the calling method, the block keeps it’s original scope
  38. Blocks and scope Even if local variables with the same

    names are defined in the calling method, the block keeps it’s original scope def run_block x = 100 y = 200 puts “run_block's x and y: #{x}, #{y}” yield end x = 10 y = 20 run_block do puts x + y end def run_block x = 100 y = 200 puts “run_block's x and y: #{x}, #{y}” yield end x = 10 y = 20 run_block do puts x + y end
  39. Blocks and scope Even if local variables with the same

    names are defined in the calling method, the block keeps it’s original scope def run_block x = 100 y = 200 puts “run_block's x and y: #{x}, #{y}” yield end x = 10 y = 20 run_block do puts x + y end def run_block x = 100 y = 200 puts “run_block's x and y: #{x}, #{y}” yield end x = 10 y = 20 run_block do puts x + y end #=> run_block's x and y: 100, 200 #=> 30 #=> run_block's x and y: 100, 200 #=> 30
  40. Blocks and self Blocks don’t just inherit the variables in

    the current scope, they also inherit the type and provide access to the object where it was defined
  41. Blocks and self Blocks don’t just inherit the variables in

    the current scope, they also inherit the type and provide access to the object where it was defined def run_block yield end run_block { puts self.class } def run_block yield end run_block { puts self.class }
  42. Blocks and self Blocks don’t just inherit the variables in

    the current scope, they also inherit the type and provide access to the object where it was defined def run_block yield end run_block { puts self.class } def run_block yield end run_block { puts self.class } #=> main #=> main
  43. Blocks as real parameters Blocks can be assigned to method

    parameters with the ampersand (&) sign
  44. Blocks as real parameters Blocks can be assigned to method

    parameters with the ampersand (&) sign def give_me_a_block(&my_block) puts my_block.class my_block.call end give_me_a_block { puts "This is a block" } def give_me_a_block(&my_block) puts my_block.class my_block.call end give_me_a_block { puts "This is a block" }
  45. Blocks as real parameters Blocks can be assigned to method

    parameters with the ampersand (&) sign def give_me_a_block(&my_block) puts my_block.class my_block.call end give_me_a_block { puts "This is a block" } def give_me_a_block(&my_block) puts my_block.class my_block.call end give_me_a_block { puts "This is a block" }
  46. Blocks as real parameters Blocks can be assigned to method

    parameters with the ampersand (&) sign def give_me_a_block(&my_block) puts my_block.class my_block.call end give_me_a_block { puts "This is a block" } def give_me_a_block(&my_block) puts my_block.class my_block.call end give_me_a_block { puts "This is a block" } #=> Proc #=> This is a block #=> Proc #=> This is a block
  47. What's a Proc? A Proc object is a block that

    can be assigned as a variable, allowing it to be re-used
  48. What's a Proc? A Proc object is a block that

    can be assigned as a variable, allowing it to be re-used square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10)
  49. What's a Proc? A Proc object is a block that

    can be assigned as a variable, allowing it to be re-used square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) The call method is used to execute a Proc object, and arguments are passed to the block via arguments to call
  50. What's a Proc? A Proc object is a block that

    can be assigned as a variable, allowing it to be re-used square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) The call method is used to execute a Proc object, and arguments are passed to the block via arguments to call
  51. What's a Proc? A Proc object is a block that

    can be assigned as a variable, allowing it to be re-used square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) The call method is used to execute a Proc object, and arguments are passed to the block via arguments to call #=> 25 #=> 100 #=> 25 #=> 100
  52. What's a Proc? A Proc object is a block that

    can be assigned as a variable, allowing it to be re-used square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) square = Proc.new do |n| n ** 2 end puts square.call(5) puts square.call(10) The call method is used to execute a Proc object, and arguments are passed to the block via arguments to call You can also create Proc objects with the proc method #=> 25 #=> 100 #=> 25 #=> 100
  53. Pass the proc You could enforce the passing of a

    Proc object instead of a block, but that's just not Ruby™
  54. Pass the proc You could enforce the passing of a

    Proc object instead of a block, but that's just not Ruby™ def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end) def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end)
  55. Pass the proc You could enforce the passing of a

    Proc object instead of a block, but that's just not Ruby™ def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end) def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end)
  56. Pass the proc You could enforce the passing of a

    Proc object instead of a block, but that's just not Ruby™ def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end) def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end)
  57. Pass the proc You could enforce the passing of a

    Proc object instead of a block, but that's just not Ruby™ def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end) def give_me_random(a_proc) a_proc.call(rand(1..10), rand(1..10)) end puts give_me_random(Proc.new do |a, b| puts “a: #{a}, b: #{b}” puts a ** b end) #=> a: 7, b: 4 #=> 2401 #=> a: 7, b: 4 #=> 2401
  58. When to use Procs Useful for re-using blocks but also

    for allowing multiple blocks to be passed to a method, for callbacks
  59. When to use Procs Useful for re-using blocks but also

    for allowing multiple blocks to be passed to a method, for callbacks def run_callbacks(procs) procs[:before].call puts "Doing my thing" procs[:after].call end run_callbacks(:before => Proc.new { puts "Executing before callback" }, :after => Proc.new { puts "Executing after callback" }) def run_callbacks(procs) procs[:before].call puts "Doing my thing" procs[:after].call end run_callbacks(:before => Proc.new { puts "Executing before callback" }, :after => Proc.new { puts "Executing after callback" })
  60. When to use Procs Useful for re-using blocks but also

    for allowing multiple blocks to be passed to a method, for callbacks def run_callbacks(procs) procs[:before].call puts "Doing my thing" procs[:after].call end run_callbacks(:before => Proc.new { puts "Executing before callback" }, :after => Proc.new { puts "Executing after callback" }) def run_callbacks(procs) procs[:before].call puts "Doing my thing" procs[:after].call end run_callbacks(:before => Proc.new { puts "Executing before callback" }, :after => Proc.new { puts "Executing after callback" })
  61. When to use Procs Useful for re-using blocks but also

    for allowing multiple blocks to be passed to a method, for callbacks def run_callbacks(procs) procs[:before].call puts "Doing my thing" procs[:after].call end run_callbacks(:before => Proc.new { puts "Executing before callback" }, :after => Proc.new { puts "Executing after callback" }) def run_callbacks(procs) procs[:before].call puts "Doing my thing" procs[:after].call end run_callbacks(:before => Proc.new { puts "Executing before callback" }, :after => Proc.new { puts "Executing after callback" }) #=> Executing before callback #=> Doing my thing #=> Executing after callback #=> Executing before callback #=> Doing my thing #=> Executing after callback
  62. What's a lambda? lambda is a method (on the Kernel

    module) that creates a Proc object with a block
  63. What's a lambda? lambda is a method (on the Kernel

    module) that creates a Proc object with a block puts lambda {}.class #=> Proc puts lambda {}.class #=> Proc
  64. What's a lambda? lambda is a method (on the Kernel

    module) that creates a Proc object with a block puts lambda {}.class #=> Proc puts lambda {}.class #=> Proc my_proc = lambda do |x| puts “Called with #{x}” end my_proc.call 10 #=> Called with 10 my_proc = lambda do |x| puts “Called with #{x}” end my_proc.call 10 #=> Called with 10
  65. What's a lambda? lambda is a method (on the Kernel

    module) that creates a Proc object with a block puts lambda {}.class #=> Proc puts lambda {}.class #=> Proc In most things, using lambda is equivalent to using Proc.new my_proc = lambda do |x| puts “Called with #{x}” end my_proc.call 10 #=> Called with 10 my_proc = lambda do |x| puts “Called with #{x}” end my_proc.call 10 #=> Called with 10
  66. Lambdas in real life Anyone use Active Record? Use lambdas

    to define scopes class User < ActiveRecord::Base attr_accessible :name, :email scope :created_before, lambda do |at| where(“created_at < ?”, at) end end #... User.created_before 3.days_ago class User < ActiveRecord::Base attr_accessible :name, :email scope :created_before, lambda do |at| where(“created_at < ?”, at) end end #... User.created_before 3.days_ago
  67. Lambdas in real life Anyone use Active Record? Use lambdas

    to define scopes class User < ActiveRecord::Base attr_accessible :name, :email scope :created_before, lambda do |at| where(“created_at < ?”, at) end end #... User.created_before 3.days_ago class User < ActiveRecord::Base attr_accessible :name, :email scope :created_before, lambda do |at| where(“created_at < ?”, at) end end #... User.created_before 3.days_ago
  68. Lambdas in real life Anyone use Active Record? Use lambdas

    to define scopes class User < ActiveRecord::Base attr_accessible :name, :email scope :created_before, lambda do |at| where(“created_at < ?”, at) end end #... User.created_before 3.days_ago class User < ActiveRecord::Base attr_accessible :name, :email scope :created_before, lambda do |at| where(“created_at < ?”, at) end end #... User.created_before 3.days_ago
  69. Proc.new !== lambda Even though lambda returns a Proc object,

    there are two differences between lambda procs and Proc.new procs
  70. Proc.new !== lambda Even though lambda returns a Proc object,

    there are two differences between lambda procs and Proc.new procs 1. Lambdas check the number of parameters passed to it
  71. Proc.new !== lambda Even though lambda returns a Proc object,

    there are two differences between lambda procs and Proc.new procs 1. Lambdas check the number of parameters passed to it 2. Return statements in lambdas return only from the block, and other blocks return from the calling method
  72. Lambdas check parameters A lambda proc must have the required

    number of parameters passed to it, whereas a standard proc is flexible
  73. Lambdas check parameters A lambda proc must have the required

    number of parameters passed to it, whereas a standard proc is flexible a_proc = Proc.new do |x| puts x.inspect end a_proc.call #=> nil a_proc = Proc.new do |x| puts x.inspect end a_proc.call #=> nil
  74. Lambdas check parameters A lambda proc must have the required

    number of parameters passed to it, whereas a standard proc is flexible a_proc = Proc.new do |x| puts x.inspect end a_proc.call #=> nil a_proc = Proc.new do |x| puts x.inspect end a_proc.call #=> nil a_lambda = lambda do |x| puts x.inspect end a_lambda.call #=> args.rb:2:in `block in <main>': wrong number of arguments (0 for 1) (ArgumentError) a_lambda = lambda do |x| puts x.inspect end a_lambda.call #=> args.rb:2:in `block in <main>': wrong number of arguments (0 for 1) (ArgumentError)
  75. Lambdas return differently A lambda returns to the context in

    which they are executing, procs return from the context in which they were defined
  76. Lambdas return differently A lambda returns to the context in

    which they are executing, procs return from the context in which they were defined def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me
  77. Lambdas return differently A lambda returns to the context in

    which they are executing, procs return from the context in which they were defined def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me
  78. Lambdas return differently A lambda returns to the context in

    which they are executing, procs return from the context in which they were defined def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me def return_lambda l = lambda do return "Now you see me" end l.call return "Now you don't!" end puts return_lambda # => Now you don't! def return_lambda l = lambda do return "Now you see me" end l.call return "Now you don't!" end puts return_lambda # => Now you don't!
  79. Lambdas return differently A lambda returns to the context in

    which they are executing, procs return from the context in which they were defined def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me def return_proc p = Proc.new do return "Now you see me" end p.call return "Now you don't!" end puts return_proc # => Now you see me def return_lambda l = lambda do return "Now you see me" end l.call return "Now you don't!" end puts return_lambda # => Now you don't! def return_lambda l = lambda do return "Now you see me" end l.call return "Now you don't!" end puts return_lambda # => Now you don't!
  80. Checking for lambdaness The Proc class has a lambda? method

    which tests the proc object for the behaviour of lambdas
  81. Checking for lambdaness The Proc class has a lambda? method

    which tests the proc object for the behaviour of lambdas a_proc = proc {} puts a_proc.lambda? a_proc = proc {} puts a_proc.lambda? If true then you know that argument handling is strict
  82. Checking for lambdaness The Proc class has a lambda? method

    which tests the proc object for the behaviour of lambdas a_proc = proc {} puts a_proc.lambda? # => false a_proc = proc {} puts a_proc.lambda? # => false If true then you know that argument handling is strict
  83. Checking for lambdaness The Proc class has a lambda? method

    which tests the proc object for the behaviour of lambdas a_proc = proc {} puts a_proc.lambda? # => false a_proc = proc {} puts a_proc.lambda? # => false a_lambda = lambda {} puts a_lambda.lambda? a_lambda = lambda {} puts a_lambda.lambda? If true then you know that argument handling is strict
  84. Checking for lambdaness The Proc class has a lambda? method

    which tests the proc object for the behaviour of lambdas a_proc = proc {} puts a_proc.lambda? # => false a_proc = proc {} puts a_proc.lambda? # => false a_lambda = lambda {} puts a_lambda.lambda? # => true a_lambda = lambda {} puts a_lambda.lambda? # => true If true then you know that argument handling is strict
  85. Passing procs as blocks What if you have a proc/lambda,

    but want to pass it to a method that accepts a block?
  86. Passing procs as blocks What if you have a proc/lambda,

    but want to pass it to a method that accepts a block? def give_me_a_block yield 10 end my_lambda = lambda { |x| puts x } give_me_a_block(&my_lambda) def give_me_a_block yield 10 end my_lambda = lambda { |x| puts x } give_me_a_block(&my_lambda) Use the ampersand (&) character to pass a proc object as a block, for methods that use yield
  87. Passing procs as blocks What if you have a proc/lambda,

    but want to pass it to a method that accepts a block? def give_me_a_block yield 10 end my_lambda = lambda { |x| puts x } give_me_a_block(&my_lambda) def give_me_a_block yield 10 end my_lambda = lambda { |x| puts x } give_me_a_block(&my_lambda) Use the ampersand (&) character to pass a proc object as a block, for methods that use yield
  88. Passing procs as blocks What if you have a proc/lambda,

    but want to pass it to a method that accepts a block? def give_me_a_block yield 10 end my_lambda = lambda { |x| puts x } give_me_a_block(&my_lambda) def give_me_a_block yield 10 end my_lambda = lambda { |x| puts x } give_me_a_block(&my_lambda) Use the ampersand (&) character to pass a proc object as a block, for methods that use yield #=> 10 #=> 10
  89. Turning methods into procs Ruby methods aren't objects, but we

    can use the to_proc method on the Symbol class to convert methods to procs...
  90. Turning methods into procs Ruby methods aren't objects, but we

    can use the to_proc method on the Symbol class to convert methods to procs... plus_sym = :+ plus_sym = :+ Create a symbol of the method you want to procify, then call the proc with the object and any parameters
  91. Turning methods into procs Ruby methods aren't objects, but we

    can use the to_proc method on the Symbol class to convert methods to procs... plus_sym = :+ plus_proc = plus_sym.to_proc #=> Proc plus_sym = :+ plus_proc = plus_sym.to_proc #=> Proc Create a symbol of the method you want to procify, then call the proc with the object and any parameters
  92. Turning methods into procs Ruby methods aren't objects, but we

    can use the to_proc method on the Symbol class to convert methods to procs... plus_sym = :+ plus_proc = plus_sym.to_proc #=> Proc puts plus_proc.call 10, 20 plus_sym = :+ plus_proc = plus_sym.to_proc #=> Proc puts plus_proc.call 10, 20 Create a symbol of the method you want to procify, then call the proc with the object and any parameters
  93. Turning methods into procs Ruby methods aren't objects, but we

    can use the to_proc method on the Symbol class to convert methods to procs... plus_sym = :+ plus_proc = plus_sym.to_proc #=> Proc puts plus_proc.call 10, 20 plus_sym = :+ plus_proc = plus_sym.to_proc #=> Proc puts plus_proc.call 10, 20 Create a symbol of the method you want to procify, then call the proc with the object and any parameters #=> 30 #=> 30
  94. Turning methods into procs We can use the ampersand operator

    when passing a symbol, which calls to_proc on that symbol and then passes it as a block
  95. Turning methods into procs We can use the ampersand operator

    when passing a symbol, which calls to_proc on that symbol and then passes it as a block puts (1..100).inject(&:+) puts (1..100).inject(&:+)
  96. Turning methods into procs We can use the ampersand operator

    when passing a symbol, which calls to_proc on that symbol and then passes it as a block puts (1..100).inject(&:+) puts (1..100).inject(&:+)
  97. Turning methods into procs We can use the ampersand operator

    when passing a symbol, which calls to_proc on that symbol and then passes it as a block puts (1..100).inject(&:+) # which is the same as puts (1..100).inject do |acc, x| acc += x end puts (1..100).inject(&:+) # which is the same as puts (1..100).inject do |acc, x| acc += x end
  98. Turning methods into procs We can use the ampersand operator

    when passing a symbol, which calls to_proc on that symbol and then passes it as a block puts (1..100).inject(&:+) # which is the same as puts (1..100).inject do |acc, x| acc += x end puts (1..100).inject(&:+) # which is the same as puts (1..100).inject do |acc, x| acc += x end Allows you to do some really concise iterations: in this case, :+.to_proc.call(acc, x) is implicitly called on every iteration
  99. Turning methods into procs We can use the ampersand operator

    when passing a symbol, which calls to_proc on that symbol and then passes it as a block puts (1..100).inject(&:+) # which is the same as puts (1..100).inject do |acc, x| acc += x end puts (1..100).inject(&:+) # which is the same as puts (1..100).inject do |acc, x| acc += x end Allows you to do some really concise iterations: in this case, :+.to_proc.call(acc, x) is implicitly called on every iteration # => 5050 # => 5050 # => 5050 # => 5050
  100. Greek Lambdas If you use UTF-8 encoding in your ruby

    files you can alias the lambda method to the actual Greek letter λ
  101. Greek Lambdas If you use UTF-8 encoding in your ruby

    files you can alias the lambda method to the actual Greek letter λ # encoding: utf-8 module Kernel alias :λ :lambda end λ { puts "hello" }.call # encoding: utf-8 module Kernel alias :λ :lambda end λ { puts "hello" }.call
  102. Greek Lambdas If you use UTF-8 encoding in your ruby

    files you can alias the lambda method to the actual Greek letter λ # encoding: utf-8 module Kernel alias :λ :lambda end λ { puts "hello" }.call # encoding: utf-8 module Kernel alias :λ :lambda end λ { puts "hello" }.call
  103. Greek Lambdas If you use UTF-8 encoding in your ruby

    files you can alias the lambda method to the actual Greek letter λ #=> hello #=> hello # encoding: utf-8 module Kernel alias :λ :lambda end λ { puts "hello" }.call # encoding: utf-8 module Kernel alias :λ :lambda end λ { puts "hello" }.call
  104. Calling procs with [] Why waste precious characters invoking the

    call method on procs? Use square brackets on the object instead.
  105. Calling procs with [] Why waste precious characters invoking the

    call method on procs? Use square brackets on the object instead. my_proc = proc { |x, y| puts “Given #{x} and #{y}” } my_proc.call(10, 20) my_proc = proc { |x, y| puts “Given #{x} and #{y}” } my_proc.call(10, 20)
  106. Calling procs with [] Why waste precious characters invoking the

    call method on procs? Use square brackets on the object instead. my_proc = proc { |x, y| puts “Given #{x} and #{y}” } my_proc.call(10, 20) my_proc[10, 20] my_proc = proc { |x, y| puts “Given #{x} and #{y}” } my_proc.call(10, 20) my_proc[10, 20]
  107. Calling procs with [] Why waste precious characters invoking the

    call method on procs? Use square brackets on the object instead. my_proc = proc { |x, y| puts “Given #{x} and #{y}” } my_proc.call(10, 20) my_proc[10, 20] my_proc = proc { |x, y| puts “Given #{x} and #{y}” } my_proc.call(10, 20) my_proc[10, 20] #=> Given 10 and 20 #=> Given 10 and 20 #=> Given 10 and 20 #=> Given 10 and 20
  108. Evaluating variables in the proc's scope You can evaluate variables

    in the scope of a proc by getting the binding and using Kernel#eval
  109. Evaluating variables in the proc's scope You can evaluate variables

    in the scope of a proc by getting the binding and using Kernel#eval def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding) def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding)
  110. Evaluating variables in the proc's scope You can evaluate variables

    in the scope of a proc by getting the binding and using Kernel#eval def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding) def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding)
  111. Evaluating variables in the proc's scope You can evaluate variables

    in the scope of a proc by getting the binding and using Kernel#eval def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding) def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding)
  112. Evaluating variables in the proc's scope You can evaluate variables

    in the scope of a proc by getting the binding and using Kernel#eval def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding) def proc_maker x = 10 y = 20 proc {} end prc = proc_maker puts eval("x + y", prc.binding) #=> 30 #=> 30
  113. Proc curry Procs can be “curried” - this allows arguments

    to be passed at different stages. The proc is executed when enough arguments have been collected
  114. Proc curry Procs can be “curried” - this allows arguments

    to be passed at different stages. The proc is executed when enough arguments have been collected my_proc = proc { |a, b, c| a + b + c } my_proc = proc { |a, b, c| a + b + c }
  115. Proc curry Procs can be “curried” - this allows arguments

    to be passed at different stages. The proc is executed when enough arguments have been collected my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1]
  116. Proc curry Procs can be “curried” - this allows arguments

    to be passed at different stages. The proc is executed when enough arguments have been collected my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] puts curried.class #=> Proc my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] puts curried.class #=> Proc
  117. Proc curry Procs can be “curried” - this allows arguments

    to be passed at different stages. The proc is executed when enough arguments have been collected my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] puts curried.class #=> Proc puts curried[2, 3] my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] puts curried.class #=> Proc puts curried[2, 3]
  118. Proc curry Procs can be “curried” - this allows arguments

    to be passed at different stages. The proc is executed when enough arguments have been collected my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] puts curried.class #=> Proc puts curried[2, 3] my_proc = proc { |a, b, c| a + b + c } curried = my_proc.curry[1] puts curried.class #=> Proc puts curried[2, 3] #=> 6 #=> 6
  119. Passing blocks to blocks Can we pass a block to

    a block? def block_to_yield yield() do puts "Trying to pass a block to a block" end end block_to_yield do yield end def block_to_yield yield() do puts "Trying to pass a block to a block" end end block_to_yield do yield end
  120. Passing blocks to blocks Can we pass a block to

    a block? def block_to_yield yield() do puts "Trying to pass a block to a block" end end block_to_yield do yield end def block_to_yield yield() do puts "Trying to pass a block to a block" end end block_to_yield do yield end
  121. Passing blocks to blocks Can we pass a block to

    a block? def block_to_yield yield() do puts "Trying to pass a block to a block" end end block_to_yield do yield end def block_to_yield yield() do puts "Trying to pass a block to a block" end end block_to_yield do yield end #=> yield.rb:3: syntax error, unexpected keyword_do #=> yield.rb:6: syntax error, unexpected keyword_end, expecting $end #=> yield.rb:3: syntax error, unexpected keyword_do #=> yield.rb:6: syntax error, unexpected keyword_end, expecting $end
  122. Passing blocks to blocks You can do it by turning

    the blocks into procs and calling them
  123. Passing blocks to blocks You can do it by turning

    the blocks into procs and calling them def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end
  124. Passing blocks to blocks You can do it by turning

    the blocks into procs and calling them def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end
  125. Passing blocks to blocks You can do it by turning

    the blocks into procs and calling them def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end
  126. Passing blocks to blocks You can do it by turning

    the blocks into procs and calling them def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end def block_to_yield(&blk) puts "Starting block_to_yield()" blk.call do puts "I'm a block in a block!" end puts "Ending block_to_yield()" end block_to_yield do |&blk| puts "Starting outer block" blk.call end #=> Starting block_to_yield() #=> Starting outer block #=> I'm a block in a block! #=> Ending block_to_yield() #=> Starting block_to_yield() #=> Starting outer block #=> I'm a block in a block! #=> Ending block_to_yield()
  127. So I leave you with this def omg(&a) a.call do

    |&c| 'ke bi' + c.call do |&e| 'cks a' + e.call + 'an' end + ' lie' end end best_var_ever = omg do |&b| 'I li' + b.call do |&d| 'g blo' + d.call do 'nd I c' end + 'not' end end puts best_var_ever def omg(&a) a.call do |&c| 'ke bi' + c.call do |&e| 'cks a' + e.call + 'an' end + ' lie' end end best_var_ever = omg do |&b| 'I li' + b.call do |&d| 'g blo' + d.call do 'nd I c' end + 'not' end end puts best_var_ever
  128. So I leave you with this def omg(&a) a.call do

    |&c| 'ke bi' + c.call do |&e| 'cks a' + e.call + 'an' end + ' lie' end end best_var_ever = omg do |&b| 'I li' + b.call do |&d| 'g blo' + d.call do 'nd I c' end + 'not' end end puts best_var_ever def omg(&a) a.call do |&c| 'ke bi' + c.call do |&e| 'cks a' + e.call + 'an' end + ' lie' end end best_var_ever = omg do |&b| 'I li' + b.call do |&d| 'g blo' + d.call do 'nd I c' end + 'not' end end puts best_var_ever #=> I like big blocks and I cannot lie #=> I like big blocks and I cannot lie