# Blocks, yield, Procs and Lambdas — Ruby

Source: https://www.geekswithgeeks.com/en/ruby/c-blocks

> Pass behaviour with blocks, write methods that yield, and use procs and lambdas.

## Ruby's signature feature

A **block** is a chunk of code passed to a method, written with `{ |x| ... }` for one-liners or `do |x| ... end` for multi-line blocks. Methods call their block with **`yield`**, check whether one was given with `block_given?`, or capture it as a parameter with `&block`. Blocks power iteration (`each`), resource management (`File.open(path) { |f| ... }` closes the file automatically), configuration DSLs and callbacks. Short blocks can use **numbered parameters** (`_1`) or, since Ruby 3.4, **`it`**: `prices.map { it * 1.18 }`. Blocks can be turned into objects. A **`Proc`** (`proc { |x| ... }` or `Proc.new`) is lenient about arguments and a `return` inside it returns from the enclosing method. A **lambda** (`->(x) { x * 2 }` or `lambda { |x| ... }`) checks its argument count strictly and `return` exits only the lambda, so lambdas behave like small anonymous methods. Both are closures, capturing variables from where they were defined, and are called with `.call`, `.()` or `[]`. `method(:name)` turns an existing method into a callable object.

## Writing a method that takes a block

yield, ensure-style cleanup and lambdas as values.

```ruby
def with_timing(label)
  started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  result = yield                                   # run the caller's block
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
  puts "#{label} took #{(elapsed * 1000).round(1)} ms"
  result
end

total = with_timing("summing") { (1..1_000_000).sum }

def each_batch(items, size)
  return to_enum(:each_batch, items, size) unless block_given?   # enumerator if no block
  items.each_slice(size) { |batch| yield batch }
end

each_batch((1..10).to_a, 4) { |b| puts b.inspect }      # [1, 2, 3, 4] [5, 6, 7, 8] [9, 10]

gst = ->(amount, rate = 0.18) { (amount * (1 + rate)).round(2) }
puts gst.call(100)         # 118.0
puts gst.(100, 0.05)       # 105.0

with_tax = [100, 250].map(&gst)                 # a lambda used as a block
upcased = %w[pen ink].map { it.upcase }         # Ruby 3.4+ 'it' parameter
```

## Handing over a recipe card

A method that yields is a cook who says "I'll prepare the pan and clean up afterwards; you tell me what to fry". The block is the recipe card you hand over at that moment.

**Quiz:** How does a lambda differ from a proc in Ruby?

- [x] Lambdas check argument counts strictly and return only from themselves
- [ ] Lambdas cannot capture variables
- [ ] Procs are faster
- [ ] There is no difference

*Answer:* Lambdas check argument counts strictly and return only from themselves. Lambdas behave like methods; procs are lenient and return from the enclosing method.
