Blocks, Procs and Lambdas

Digging into these extremely useful and potentially head-scratching anonymous functions

Scroll down...

Content

Resources

Comments

In this lesson, you'll learn more about blocks and also about their lessor-known cousins, Procs, lambdas and Methods.

Our goal is for you to become comfortable working with blocks and writing your own methods that take them. You should also understand when you may need to use a Proc instead and how that works. Finally, you should absorb the high level principles of lambdas and Methods (though you won't need to implement them yourself).

Blocks

One of the most confusing parts of learning basic Ruby (until your AHA! moment) is understanding what blocks are and how they work, mostly because they're something you probably haven't ever seen before trying out Ruby. But you've already seen them used many times before... they are commonly used as inputs to some of the iterators you've no doubt worked with like each or map.

Blocks are actually quite simple. They're just chunks of code that you can pick up and drop into another method as an input. They're often called "anonymous functions" because they have no name but behave much like functions.

You can think of them like "helper" functions because you don't find blocks just hanging around without some method (like each) using them.

Declaring a Block

You declare a block using squiggly braces {} if it's on one line or do ... end if it's on multiple lines. This is by convention... you can use either one if you really want (but don't):

> [1,2,3].each { |num| print "#{num}! " }
1! 2! 3! #=>[1,2,3]
> [1,2,3].each do |num|
>    print "#{num}!"
> end
1! 2! 3! #=>[1,2,3]         # Identical to the first case.

Block Returns

Just like methods, some blocks take inputs, others do not. Some return important information, others do not. Blocks let you use the implicit return (whatever's on the last line) but NOT the return method (which throws an error):

# ERROR DANGER WHOOP WHOOP!
> [1,2,3].each do |num|
>    print "#{num}!"
>    return "returning this explicitly"
> end
1!#LocalJumpError: unexpected return

If you use the implicit return, it doesn't change the return of the method you provided the block to. For instance, each will always return the original array regardless of what the block thinks it is returning:

> [1,2,3].each do |num|
>    print "#{num}!"
>    "returning this implicitly"
> end
1!2!3! #=> [1,2,3]

In that case, the block's return is meaningless and gets buried inside each never to be seen again.

But some other methods do use the return value of the block in order to inform their behavior, for instance the map method which we'll cover in the next lesson. This method takes the return value of the block for each iteration and puts it into the array that it returns when all is complete:

> [1,2,3].map do |num|
>    num**2  # implicit return for the block
> end
#=> [1,4,9]

Using Blocks with Yield

Blocks are used as arguments to other methods (like each), just like the normal arguments that you see between the parentheses... they are just always listed last and on their own because they tend to take up multiple lines. Don't think of them as anything too special. The each method isn't special either, it's just built to accept a block as an argument.

How does each take a block then? Through the magic of the yield statement, which basically says "run the block you gave me right here".

Fun Facts:

  1. You actually don't even need to say that you want to accept a block to your method... the block will just be there waiting for you when you call yield inside your method.
  2. yield can pass parameters to your block as well.

It's best to see it in action. In this case, we've lifted the hood and created our own made-up version of the each method to get an idea of what's happening:

# my_script.rb
def my_each(arr)
    i = 0
    while i < arr.size
        yield(arr[i])
        i+=1
    end
    arr
end

# IRB
> require './my_script.rb'
> my_each([1,2,3]) { |num| print "#{num}! " }
1! 2! 3!
#=> [1,2,3]

Note that my_each takes the array as an input instead of being called on the array itself because we haven't covered "monkey patching" classes yet.

As you can see, we simply iterate over the provided array and run the block every time the code hits the yield method. The yield method assumes the block takes an input (which it does) and passes it the current item in the array. Last, we return the original array because that's what the "real" each does.

Our my_each doesn't look too different from Ruby's each:

> each([1,2,3]) { |num| print "#{num}! " }
1! 2! 3! #=> [1,2,3]

If you wanted to create an almost-exact copy of Ruby's each, you just have to "monkey patch" your my_each method onto the Array class. We'll talk about this later, but it looks like:

class Array
  def my_each
    i = 0
    while i < self.size
        yield(self[i])   # Our block gets "subbed in" here
        i+=1
    end
    self
  end
end
A Sneaky Conceptual Trick

Having a bit of a mind warp trying to understand how you run a block with yield? Try this trick.

Think of your block as just another Ruby method (because that's pretty much all it is). Because you never gave it a name, assume that its name is yield. That's it. Now it should be more obvious that when you use yield, you're just calling your block and passing it any arguments like you would with any other method.

Just remember that the only wrinkle is that blocks can't use the return statement and use an implicit return instead (the last line is returned).

Why Blocks are Useful

One reason blocks are great is because you can write a generic method like each and leave it up to the user who actually calls each to provide its functionality. each simply wraps your block in some code that says what to do with it.

Another use case is when creating methods where you want to optionally be able to override how they "work" internally by supplying your own block.

As an example of this, sort lets you supply your own block to determine how to actually order the items of the array if you want to.

sort is quite particular about what it wants -- your block needs to take both the "current" item and the "next" item to be compared with. Then it needs to return 1, 0, or -1 depending on whether these items are greater, less than, or equal to each other. This is done with the spaceship operator <=>.

> ["howdy", "pardner", "fella"].sort do |current_item, next_item|
>     current_item[1] <=> next_item[1] # compare second letters
> end
#=> ["pardner", "fella", "howdy"] 

You just need to consult the Ruby documentation to know how exactly a method expects to use its block.

Was a Block Passed?

If you want to ask whether a block was passed at all (to only yield in that case), use block_given?, or rather: yield if block_given?

def block_hungry
    puts "YUMMY BLOCKS" if block_given?
    yield if block_given?
end

#IRB
> block_hungry { puts "tasty block here!" }
YUMMY BLOCKS
tasty block here
=> nil
> block_hungry   # no block given... sad :(
# => nil    

A lot of beginners just blindly take it on faith that each and map and select etc. all work the way they do. You're more skeptical than that, which is good. They're really quite simple and you should start to see how they might work.

Luckily, you'll get a chance to build your own soon enough...

Procs

What if you want to pass TWO blocks to your function? What if you want to save your block to a variable so you can use it again later? That's a job for Procs, aka Procedures! Actually, a block is a Proc... Proc is just the class name for a block.

They rhyme just to confuse you.

The block is sort of like a stripped-down and temporary version of a Proc that Ruby included just to make it really easy to use things like those each iterators.

Creating and Saving a Proc

A Proc is just a block that you can save to a variable, thereby giving it a bit more permanence:

> my_proc = Proc.new { |arg1| print "#{arg1}! " }
#=> #<Proc:0x007fdc34133ef0@(irb):24>

Using Procs

Now that you've got your block (called a Proc now) saved to a variable, it's much easier to pass around and use! Use that Proc as an input to a function exactly like you otherwise would (except here we precede it with & so the function knows not to treat it as a normal argument):

> my_proc = Proc.new { |num| print "#{num}! " }
#=> #<Proc:0x007fdc34133ef0@(irb):24>
> [1,2,3].each( &my_proc )
1! 2! 3! #=>[1,2,3]

Yep, that's right, each can also take a Proc directly as an argument (because what's the difference, really?). It's almost exactly the same as passing in a block like normal!

Accepting Procs In Your Functions

When you create your own function to accept procs, the guts need to change a little bit because you'll need to use call instead of yield inside. This makes sense because how would you know which Proc to yield to if there were multiple?

call literally just runs the Proc that is called on. You can optionally give it arguments to pass into the Proc, just like yield:

> my_proc = Proc.new { |arg1| print "#{arg1}! " }
#=> #<Proc:0x007fdc34133ef0@(irb):24>
> my_proc.call("howdy ")
howdy #=> nil

Most of the time, using a block is more than sufficient, especially in your early projects. Once you start seeing the need for using a Proc (like passing multiple arguments or saving it for later as a callback), you'll have Procs there waiting for you.

Using Blocks as Procs to Save Energy

So you need to call a block using yield Procs using call... isn't that a lot to remember? Why not just call blocks using call as well?

Because Ruby loves you, it allows you to accept a block into your method and then "Proc-ify" it (okay, we made that word up). Basically, you use the ampersand (&) to convert your block argument into a Proc and then treat it like any other:

class Array
  def my_each( &now_it_is_a_proc )

    self.length.times do |i|

      # Note that we only use the `&` in the method signature
      # and now we can call our new Proc like normal
      now_it_is_a_proc.call( self[i] )

    end
  end
end
[1,2,3].my_each { |i| puts i*2 }

Taking Either a Block or a Proc

You probably won't see this much in the real world but what if you want to write a method which can take either a block or a Proc?

As long as we can figure out how to know if a block has actually been passed, we shouldn't have any problems using a bit of conditional logic to do so. Luckily, Ruby gives you block_given? to determine if a block was passed to a method:

# We make the proc optional so it works when a block only is passed
def block_proc( proc_argument = nil )
  if block_given?
    yield
  else
    proc_argument.call
  end
end

my_proc = Proc.new { puts "Proc!" }
block_proc( my_proc )
Proc!
#=> nil

block_proc { puts "Block!" }
Block!
#=> nil

Closures

Blocks and Procs are actually both a type of "closure". A closure is basically a formal, computer-science-y way of saying "a chunk of code that you can pass around but which hangs onto the variables that you gave it when you first called it".

That pretty accurately describes blocks and Procs, doesn't it?

Lambdas and Methods

There are two other similar closures to be aware of but about which you certainly don't need to be an expert because they're used in less typical applications.

Lambdas

The first of these is a lambda. If Procs are sort of a more-fleshed-out version of blocks, then lambdas are sort of a more-fleshed-out version of Procs. They are one step closer to being actual methods themselves, but still technically count as anonymous functions. If you're coming from JavaScript, anonymous functions shouldn't be anything new to you but they probably still seem a bit odd to the new Rubyist.

It's easier to define a Lambda in terms of how it's different from a Proc. We said that a lambda acts more like a real method but what does that mean?

  • A lambda gives you more flexibility with what it returns (like if you want to return multiple values at once) because you can safely use the explicit return statement inside of one. With lambdas, return will only return from the lambda itself and not the enclosing method, which is what happens if you use return inside a block or Proc.
  • Lambdas are also much stricter than Procs about you passing them the correct number of arguments (you'll get an error if you pass the wrong number).

Here's a simple example to show you the syntax of a lambda:

> my_lam = lambda do |word| 
>   puts word
>   return word            # you can do this in lambdas not Procs
> end
#=> #<Proc:0x007fdc340b92b8@(irb):25 (lambda)>
> my_lam.call("howdy ")
howdy #=> "howdy "        # not nil because we gave it a return

As you can see from the object returned by the lambda creation, a lambda is ALSO a form of the Proc class.

Methods

The second additional closure is called a Method because, well, it's the closest of the four (blocks, Procs, lambdas, and Methods) to an actual method. Which it is. These are not common at all, so consider this an aside.

"Method"'s (capitalized because they're actually a class of their own) are really just a convenient way to pass a normal method to another normal method by wrapping the symbol of its name in the word method().

So what? To use the same example as we have been so far:

Edit: Note that my_each has been modified for this example to now take an argument for illustration purposes. The standard each does not.

class Array
  def my_each(some_method)
    i = 0
    while i < self.size
      some_method.call(self[i])
      i+=1
    end
  end
  self
end

def print_stuff(word)
  print "#{word}! "
end

> [1,2,3].my_each(method(:print_stuff))    # symbolize the name!
1! 2! 3! => nil

Code Review

The important bits of code from this lesson

# One line block
[1,2,3].each { |num| print "#{num}! " }

# Multi-line block
[1,2,3].each do |num| 
  print "#{num}! " 
end

# Yielding to a block
def method_taking_block
  yield("some argument for the block") if block_given?
end

# Create and use a Proc
my_proc = Proc.new { |arg1| print "#{arg1}! " }
[1,2,3].each(&my_proc)

# Call a Proc inside a method
# Sort of like `yield`ing
my_proc.call("Some Argument") 

# Make a new Lambda (just like Procs)
my_lam = lambda { |word| puts word }

# Use a Method
method(:print_stuff_method).call("something to print")

Wrapping Up

TADA! We've now gotten our worthless array of numbers printed out 4 different ways, each seemingly less useful than the last!

Fear not. Learn your blocks cold and have a good handle on Procs (which should be easy since they're basically the same thing) but just keep lambdas and Methods in the back of your head for much later.

So...

  • Blocks are unnamed little code chunks you can drop into other methods. Used all the time.
  • Procs are identical to blocks but you can store them in variables, which lets you pass them into functions as explicit arguments and save them for later. Used explicitly sometimes.
  • Lambdas are really full methods that just haven't been named. Used rarely.
  • Methods are a way of taking actual named methods and passing them around as arguments to or returns from other methods in your code. Used rarely.
  • Closure is just the umbrella term for all four of those things, which all somehow involve passing around chunks of code.


Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

Sorry, comments aren't active just yet!

Next Lesson: The Enumerable Module