Skip to content

Blocks, Procs and Lambdas

datanorris edited this page Jan 5, 2016 · 6 revisions

In Ruby syntax, there are 3 basic types of "units of code" i.e. chunks of code that are executed top to bottom when they are invoked.

  • The whole Ruby program, i.e. usually the whole contents of a Ruby source file, executed when it is loaded. This definition also applies to any Ruby source files that are loaded (e.g. via the require method) as part of another Ruby program.
  • Methods, executed when the method is called
  • Blocks, executed when the block is called

A block is a bit like an anonymous method - it's a chunk of code which accepts method-like arguments and returns a value, but it doesn't have a name and is not defined on a class. However, there are some important differences.

Key characteristics of blocks:

  • A block is not a Ruby value - it's not an Object, it can't be assigned to a variable, can't be used as an operand for operators, can't be passed to methods as a regular value argument etc. Rather, a block is intrinsically a part of the syntax and mechanics of a method call
  • If you wish to treat the block as an object, it is possible to do so - a block can be wrapped in an instance of the Proc class (i.e. a Proc) e.g. via a block formal argument
    • A block within a Proc can be invoked by using the Proc's call method
  • A Proc can be unwrapped again into a block passed to a method via a block call argument
  • A block can be invoked with call arguments the same as a method
  • A block can accept formal arguments similar to a method, with some slight differences
  • A block has a return value
  • A block internally has "lambda" flag, which when set, alters the behaviour of the block slightly:
    • A block with lambda flag set accepts formal arguments the same way as a method
    • A block with lambda flag set has slightly different semantics for Ruby control transfer statements, such as return
  • A block can be manipulated in the following specific ways (excluding lambda expressions, see below):
    • A block is created in a method call, and passed to the called method for it to use, either by specifying a block or a block call argument in the method call
    • A method can invoke its passed block with the yield statement
    • A method can use other Ruby statements which do things with the block, such as super
    • A method can wrap its passed block in a Proc by accepting a block formal argument
    • A method can call specialized core methods that manipulate the passed block, such as block_given? which tests for the presence of a block

A Proc is an instance of the Proc class, whose purpose is to wrap a Block inside an object so it can be treated as an object. A Proc is therefore closer to what you might think of as an "anonymous method", especially if its block has the lambda flag set.

A block with lambda flag set is typically created by:

  • the lambda method, which accepts a block and returns a Proc with that block and its lambda flag set e.g .lambda { |arg| puts arg }
  • equivalently, you can use the -> lambda expression syntax e.g. -> (arg) { puts arg }. Technically, this is a 4th type of unit of code possible in the Ruby syntax, however its more useful to think of this as syntax shorthand for the lambda method

A "lambda" is a little tricky to define:

  • Normally, a "lambda" refers to a Proc created on a block with lambda flag set, however:
  • If you create such a Proc e.g. with lambda or -> and then unwrap it by passing it to a method via a block call argument, is the block thus passed (which has lambda flag set) a lambda? I will say yes, and use the term "lambda" interchangeably for these two concepts. If needed, I'll distinguish them by describing "lambda Procs" and "lambda blocks".

Clone this wiki locally