Tips FROM

The RuBY CLOSUREs BOOK

I'm Guo Xiang

I work for

https://leanpub.com/therubyclosuresbook

1. Free VARIABLES

In computer programming, the term free variable refers to variables used in a function that are neither local variables nor parameters of that function.

source: https://en.wikipedia.org/wiki/Free_variables_and_bound_variables

1. Free VARIABLES

snorlax_sighted = lambda do |coordinates|
  lambda do
    puts "SNORLAX!!! Chiong to #{coordinates}"
  end
end

1. Free VARIABLES

snorlax_sighted = lambda do |coordinates|
  lambda do
    puts "SNORLAX!!! Chiong to #{coordinates}"
  end
end

1. Free VARIABLES

snorlax_sighted = lambda do |coordinates|
  lambda do
    puts "SNORLAX!!! Chiong to #{coordinates}"
  end
end

2. Closures

snorlax_sighted = lambda do |coordinates|
  lambda do
    puts "SNORLAX!!! Chiong to #{coordinates}"
  end
end

2. Closures

"a) a function ... b) whose body references some variable that is defined in a parent scope"

source: The Ruby Closures Book

3. Yield

Title Text

def snorlax
  yield "Hyper Beam", "Sleep"
end

=> snorlax do |attack_1, attack_2| 
=>   puts "Snorlax attacks: #{attack_1}, #{attack_2}"
=> end

3. Yield

Title Text

def snorlax
  yield "Hyper Beam", "Sleep"
end

=> snorlax
irb(main):008:0> snorlax
LocalJumpError: no block given (yield)
	from (irb):6:in `snorlax'
	from (irb):8

3. Yield

Title Text

irb(main):023:0> method :puts
=> #<Method: Object(Kernel)#puts>
irb(main):025:0> method :yield
NameError: undefined method `yield' for class `Object`
	from (irb):25:in `method'
	from (irb):25

3. block_given?

Title Text

def snorlax
  if block_given?
    yield "Hyper Beam", "Sleep"
  end
end

=> snorlax
nil

3. Yield

Title Text

def snorlax
  yield "Hyper Beam", "Sleep"
end

irb=> snorlax do |attack_1|
irb=>   puts attack_1
irb=> end
Hyper Beam

3. Yield

Title Text

def snorlax
  yield "Hyper Beam"
end

irb=> snorlax do |attack_1, attack_2|
irb=>   puts attack_1
irb=>   puts attack_2
irb=> end
Hyper Beam

irb=> nil

Ex1. Implement Times

Title Text

class Fixnum
  def times
    count = 0

    while count < self
      puts count
      yield
      count += 1
    end

    self
  end 
end

5.times { puts "Yay!" }

Ex1. Implement Times

Title Text

class Fixnum
  def times
    count = 0

    while count < self
      puts count
      yield
      count += 1
    end

    self
  end 
end

5.times { puts "Yay!" }

Ex1. Implement Times

Title Text

class Fixnum
  def times
    count = 0

    while count < self
      puts count
      yield
      count += 1
    end

    self
  end 
end

5.times { puts "Yay!" }

Ex1. Implement Times

Title Text

class Fixnum
  def times
    count = 0

    while count < self
      puts count
      yield
      count += 1
    end

    self
  end 
end

5.times { puts "Yay!" }

Ex1. Implement Times

Title Text

class Fixnum
  def times
    count = 0

    while count < self
      puts count
      yield
      count += 1
    end

    self
  end 
end

5.times { puts "Yay!" }

ex2. Implement EACH

Title Text

class Array
  def each
    index = 0

    while index < self.length
      puts "Yielding: #{self[index]}"
      yield self[index]
      index += 1
    end
  end 
end

[1, 2, 3].each { |x| puts "Yay!" * x }

ex2. Implement EACH

Title Text

class Array
  def each
    index = 0

    while index < self.length
      puts "Yielding: #{self[index]}"
      yield self[index]
      index += 1
    end
  end 
end

[1, 2, 3].each { |x| puts "Yay!" * x }

ex2. Implement EACH

Title Text

class Array
  def each
    index = 0

    while index < self.length
      puts "Yielding: #{self[index]}"
      yield self[index]
      index += 1
    end
  end 
end

[1, 2, 3].each { |x| puts "Yay!" * x }

ex2. Implement EACH

Title Text

class Array
  def each
    index = 0

    while index < self.length
      puts "Yielding: #{self[index]}"
      yield self[index]
      index += 1
    end
  end 
end

[1, 2, 3].each { |x| puts "Yay!" * x }

ex2. Implement EACH

Title Text

class Array
  def each
    index = 0

    while index < self.length
      puts "Yielding: #{self[index]}"
      yield self[index]
      index += 1
    end
  end 
end

[1, 2, 3].each { |x| puts "Yay!" * x }

4. INSTANCE_EVAL To the RESCUE!

MyRouter.new do
  get "/users"
  post "/user/:id"
end

=> GET: /users
=> POST: /users/:id

4. INSTANCE_EVAL To the RESCUE!

class MyRouter
  def initialize(&block)
    instance_eval(&block)
  end

  def get(endpoint)
    puts "GET: #{endpoint}"
  end

  def post(endpoint)
    puts "POST: #{endpoint}"
  end
end

4. INSTANCE_EVAL To the RESCUE!

MyRouter.new do
  self.get "/users"
  self.post "/user/:id"
end

=> GET: /users
=> POST: /users/:id

5. Calling PROCS

my_proc = proc { |x, y| x + y }

my_proc.call('Snor', 'lax')
=> "Snorlax"

5. Calling PROCS

my_proc = proc { |x, y| x + y }

my_proc.('Snor', 'lax')
=> "Snorlax"

class MyKlass
  def call(pokemon)
    "I choose you #{pokemon}"
  end
end

MyKlass.new.("Snorlax")
=> "I choose you Snorlax"

5. Calling PROCS

my_proc = proc { |x, y| x + y }

my_proc['Snor', 'lax']
=> "Snorlax"

5. Calling PROCS

my_proc = proc { |x, y| x + y }

my_proc === ['Snor', 'lax']
=> "Snorlax"

case ['Snor', 'lax']
when my_proc
  puts "I found a Snorlax"
else
 # Do nothing
end

=> "I found a Snorlax"

6. `symbol#to_proc`

words = %w{one two three}

words.each(&:upcase!)
=> ["ONE", "TWO", "THREE"]

words.each(&(:upcase!))

6. `symbol#to_proc`

words = %w{one two three}

my_proc = proc { |x| x.upcase! }
words.map(&my_proc)

words.map { |x| x.upcase! }
=> ["ONE", "TWO", "THREE"]

class Pokemon
  def to_proc
    proc { |x| x.downcase! }
  end
end

words.map(&(Pokemon.new))
=> ["one", "two", "three"]

6. `symbol#to_proc`

words = %w{one two three}

my_proc = proc { |x| x.upcase! }
words.map(&my_proc)

words.map { |x| x.upcase! }
=> ["ONE", "TWO", "THREE"]

class Pokemon
  def to_proc
    proc { |x| x.downcase! }
  end
end

words.map(&(Pokemon.new))
=> ["one", "two", "three"]

6. `symbol#to_proc`

words = %w{one two three}

words.map(&:upcase)

class Symbol
  def to_proc 
    proc do |object|
      puts "Sending #{self} to #{object}" 
      object.public_send(self)
    end
  end
end

words.map(&:upcase)

6. `symbol#to_proc`

words = %w{one two three}

words.map(&:upcase)

class Symbol
  def to_proc 
    proc do |object|
      puts "Sending #{self} to #{object}" 
      object.public_send(self)
    end
  end
end

words.map(&:upcase)

6. `symbol#to_proc`

words = %w{one two three}

words.map(&:upcase)

class Symbol
  def to_proc 
    proc do |object|
      puts "Sending #{self} to #{object}" 
      object.public_send(self)
    end
  end
end

words.map(&:upcase)

6. `symbol#to_proc`

numbers = [1, 2, 3]

numbers.inject(0) { |result, element| result + element }

class Symbol
  def to_proc
    proc do |obj, args|
      puts "obj: #{obj}"
      puts "args: #{args}"
      obj.public_send(self, *args)
    end
  end
end

7. `Proc#curry`

my_proc = proc { |a, b, c| a * b * c }.curry
my_proc.call(1).call(2).call(3)
=> 6

7. `Proc#curry`

I consider this method (Proc#curry) to be trivial and should be
treated like an Easter egg for functional programming kids.
						matz.

8. LAMBDAS VS PROC

lambda { |x, y| x + y }.(1, 2)
=> 3

lambda { |x, y| x + y }.(1)
=> wrong number of arguments (given 1, expected 2)

proc { |x, y| x + y }.(1, 2)
=> 3

proc { |x, y| x + y }.(1)
=> TypeError: nil can't be coerced into Fixnum

8. LAMBDAS VS PROC

def snorlax(proc_or_lambda)
  puts "I see a snorlax"
  proc_or_lambda.call
  puts "Snorlax ran away" 
end

snorlax(lambda { puts "Throw pokeball"; return })
=> I see a snorlax
=> Throw pokeball
=> Snorlax ran away

snorlax(proc { puts "Throw pokeball"; return })
=> LocalJumpError: unexpected return

def test
 puts "Looking for snorlax"
 snorlax(proc { puts "Throw pokeball"; return })
 puts "Looking for another snorlax"
end

=> Looking for snorlax
=> I see a snorlax
=> Throw pokeball

8. LAMBDAS VS PROC

def snorlax(proc_or_lambda)
  puts "I see a snorlax"
  proc_or_lambda.call
  puts "Snorlax ran away" 
end

snorlax(lambda { puts "Throw pokeball"; return })
=> I see a snorlax
=> Throw pokeball
=> Snorlax ran away

snorlax(proc { puts "Throw pokeball"; return })
=> LocalJumpError: unexpected return

def test
 puts "Looking for snorlax"
 snorlax(proc { puts "Throw pokeball"; return })
 puts "Looking for another snorlax"
end

=> Looking for snorlax
=> I see a snorlax
=> Throw pokeball

8. LAMBDAS VS PROC

def snorlax(proc_or_lambda)
  puts "I see a snorlax"
  proc_or_lambda.call
  puts "Snorlax ran away" 
end

snorlax(lambda { puts "Throw pokeball"; return })
=> I see a snorlax
=> Throw pokeball
=> Snorlax ran away

snorlax(proc { puts "Throw pokeball"; return })
=> LocalJumpError: unexpected return

def test
 puts "Looking for snorlax"
 snorlax(proc { puts "Throw pokeball"; return })
 puts "Looking for another snorlax"
end

=> Looking for snorlax
=> I see a snorlax
=> Throw pokeball

8. LAMBDAS VS PROC

def snorlax(proc_or_lambda)
  puts "I see a snorlax"
  proc_or_lambda.call
  puts "Snorlax ran away" 
end

snorlax(lambda { puts "Throw pokeball"; return })
=> I see a snorlax
=> Throw pokeball
=> Snorlax ran away

snorlax(proc { puts "Throw pokeball"; return })
=> LocalJumpError: unexpected return

def test
 puts "Looking for snorlax"
 snorlax(proc { puts "Throw pokeball"; return })
 puts "Looking for another snorlax"
end

=> Looking for snorlax
=> I see a snorlax
=> Throw pokeball

More examples in the booK

Questions?

TIPS FROM THE RUBY CLOSURES BOOK

By tgxworld

TIPS FROM THE RUBY CLOSURES BOOK

  • 1,010