Back to Home

Blocks, Procs and Lambdas

Date: Jan 22, 2015

Ruby's Methods

Before start talking about Blocks, Procs and Lambdas, maybe it is worth to see why ruby is called an "Object-Oriented" Language

While I was learning about how to use ruby's Class, it becomes NOT so clear that why we use Class and why it is so special? And why suddenly everyone talks about 'object oriented language'? When I deal with String and Fixnum - this was much easier for me to undertnad that 'object' means. Objects have behavior and may contain data, data to which they alone control access.

Blocks

Blocks are one of ruby's most powerful features. In fact, blocks are actually a type of 'syntax' in the ruby language. They are a little bit different from objects and method definitions. Blocks are one of the very few exceptions to the "everything is an object" rule in Ruby. Blocks are not objects, and they can't be saved to variables. block can be expressed by 'do...end' or { }. Passing blocks is one way to pass functions as arguments to other functions. The difference is that the curly braces have a higher priority inside of the ruby interpreter.

# example 1 def print(&block) puts "hello world" block.call end puts print{puts "hello again"} # example 2 def some_adding(a, b, my_proc) my_block.call(a, b) end add = proc { |x, y| x + y } some_adding(8, 9, add) #=> 25 # The block is expressed with '&'. It doesn't execute unless you 'call' them # The proc add is passed as a parameter to the method some_adding.

This example itself isn't very useful unless we like to explore many different ways to print out stuff. The reason block is a powerful tool is that there are many useful 'keywords' like 'BEGIN', 'END', 'rescue', 'raise' and 'ensure'. Let's see some examples

# EXAMPLE 1. rescue, ensure, raise, BEGIN and END def header(&block) puts "I am a header" block.call # rescue block will rescue from 'raised' error thus error message will not be printed rescue puts "This is a rescue!!" # No matter what happens 'ensure' block will run after error message ensure puts "This is the last sentence" end header { puts "This is inside of the header" raise "This is an error message" message and it will stop code running forward } BEGIN {puts "THIS IS FROM BEGIN BLOCK"} END {puts "THIS IS END BLOCK"} => THIS IS FROM BEGIN BLOCK I am a header This is inside of the header This is a rescue!! This is the last sentence THIS IS END BLOCK

Here is also way to use begin-end block. It can be expressed as a simple block of code.

Yield

You invoke a block by using the yield statement. You can pass the argument(parameters) for invoking the block. Let's see the example. In this example, 'name' is given as a parameters for invoking call_name block. You can pass as many parameters you wish and it can be used in block between two pipes("||", in this case "|name|").

def call_name(&block) puts "What is your name?" yield name = gets.chomp end call_name { |name| puts "Your name in upcase is #{name.upcase}" puts "Your name is #{name}" } => What is your name? sarah Your name in upcase is SARAH Your name is sarah

The great thing about 'yield' statment is that it gives control of the current method over the block that is passed in. Yield can also be used to send a variable value in to, or out of, a block of code. It also does not change the original variable so although the 'name' variable is same it will not upcase the original name 'sarah'.

# Example3. This is how we can use block in real life class Speech attr :title def initialize print "What is the speech title?" @title = gets.chomp @lines = [] while add_line puts "Line added." end end def add_line puts "Add line: (blank line to exit)" line = gets.chomp if line.length > 0 @lines << line return line else return nil end end def each(&block) @lines.each { |line| yield line } end end my_speech = Speech.new my_speech.each {|line| puts "#{my_speech.title} : #{line}" } => What is the speech title?Test Speech Add line: (blank line to exit) Sarah is awesome person. Line added. Add line: (blank line to exit) Sarah is going to be an awesome programmer. Line added. Add line: (blank line to exit) Test Speech : Sarah is awesome person. Test Speech : Sarah is going to be an awesome programmer.

One very important lesson here. If you forget to put attr :title, this code will not run and give you error on undefined local veriable or method 'title'. Very important to put attr reader if you want to call the method outside of the class.

Procs and Lambdas

Proc objects are blocks of code that can be bound to a set of local variables. You can think of a proc object as a "saved" block. Procs are a great way of keeping your code DRY.

They are blocks of code that can be assigned to variables. Proc (short for Process) is more meaningful and broad concept including blocks and lambdas. Therefore many rubist write in uupcase 'Procs' but in lowercase 'blocks' and 'lambdas'. It is also because blocks and lambdas do not have their own class.

#How to create procs my_proc = Proc.new {} # or => #< Proc:0x007fa2790e5dd0@(irb):15 > my_proc = proc # No argument will cause Argument Error. => ArgumentError: tried to create Proc object without a block my_proc = proc {} => #< Proc:0x007fa278b1ecf8@(irb):18 > multifying = proc { |x| x.inject(&:+) } multifying.call([1,2,3]) #=> 6proc_square_number = proc { |x| x * x } proc_sum_array = proc { |x| x.inject(&:+) } #How to create lambdas my_lambda = lambda {} # or my_lambda -> lambda {} #'skinny arrow' can be used to create lambdas => #< Proc:0x007fa278b2fdf0@(irb):20 (lambda) > #Difference when you create lambda is that it will say (lambda).

Here is a example of using skinny lambda in model scope. The important component is the "-> {}"

So why use Procs and lambda? Basically they are 'keywords'. Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables. This is probably how Ruby's class and its instance variables are set.

class AddressBook def initialize(&block) yield self if block_given? end def set_variable return proc { |kind, value| [kind, value].join(": ") } end def name(value) @name = set_variable.call "Name", value end def phone_number(value) @phone_number= set_variable.call "Phone_Number", value end def e_mail(value) @e_mail = set_variable.call "E-Mail", value end def display puts @name puts "_____________" puts @phone_number puts "_____________" puts @e_mail puts "_____________" end end brian_addressbook = AddressBook.new { |a| a.name "Brian theDog" a.phone_number "777-666-5555" a.e_mail "brian@thedog.com" } brian_addressbook.display => Name: Brian theDog _____________ Phone_Number: 777-666-5555 _____________ E-Mail: brian@thedog.com _____________

Difference between procs and lambda is that when you call procs : It returns from inside of procs lambdas : It has diminutive return like Ruby. While proc return will stop a method and return the value provided (inside of the proc), lambdas will return their value TO the method and let the method continues on.

def procs_return variable = proc {return "I am procs and this is from the inside of procs"} variable.call return "This is from outside of the procs_return method" end def lambdas_return variable = lambda {return "I am lambda and this is from the inside of lambda"} variable.call return "This is from outside of the lambda_return method" end puts procs_return puts "___________" puts lambdas_return => I am procs and this is from the inside of procs ___________ This is from outside of the lambda_return method # proc will return inside of proc # lambda will continue the method and return the final return

Closure

Blocks, Procs and Lambdas are closures in Ruby. Closure is a function/method that can be passed around like an object and remembers the value of variables no longer in scope.