| name | ruby-blocks-procs-lambdas |
| user-invocable | false |
| description | Use when working with Ruby blocks, procs, lambdas, and functional programming patterns including closures and higher-order functions. |
| allowed-tools | ["Bash","Read","Write","Edit"] |
Ruby Blocks, Procs, and Lambdas
Master Ruby's functional programming features with blocks, procs, and lambdas. These are fundamental to Ruby's expressive and elegant style.
Blocks
Basic Block Syntax
[1, 2, 3].each do |num|
puts num * 2
end
[1, 2, 3].each { |num| puts num * 2 }
Yielding to Blocks
def repeat(times)
times.times do
yield
end
end
repeat(3) { puts "Hello" }
def greet
yield("World")
end
greet { |name| puts "Hello, #{name}!" }
Block Arguments
def process_data(data)
result = yield(data)
puts "Result: #{result}"
end
process_data(10) { |x| x * 2 }
Checking for Blocks
def optional_block
if block_given?
yield
else
puts "No block provided"
end
end
optional_block { puts "Block executed" }
optional_block
Block Local Variables
x = 10
[1, 2, 3].each do |num; local_var|
local_var = num * 2
puts local_var
end
puts x
Procs
Creating Procs
my_proc = Proc.new { |x| x * 2 }
puts my_proc.call(5)
my_proc = proc { |x| x * 2 }
my_proc = ->(x) { x * 2 }
Proc Characteristics
flexible_proc = Proc.new { |x, y| "x: #{x}, y: #{y}" }
puts flexible_proc.call(1)
puts flexible_proc.call(1, 2, 3)
def proc_return
my_proc = Proc.new { return "from proc" }
my_proc.call
"after proc"
end
puts proc_return
Passing Procs as Arguments
def execute_proc(my_proc)
my_proc.call
end
greeting = Proc.new { puts "Hello from proc!" }
execute_proc(greeting)
Converting Blocks to Procs
def method_with_proc(&block)
block.call
end
method_with_proc { puts "Block converted to proc" }
Lambdas
Creating Lambdas
my_lambda = lambda { |x| x * 2 }
my_lambda = ->(x) { x * 2 }
my_lambda = ->(x) do
result = x * 2
result + 1
end
puts my_lambda.call(5)
Lambda Characteristics
strict_lambda = ->(x, y) { x + y }
strict_lambda.call(1, 2)
def lambda_return
my_lambda = -> { return "from lambda" }
my_lambda.call
"after lambda"
end
puts lambda_return
Lambda with Multiple Arguments
add = ->(x, y) { x + y }
multiply = ->(x, y, z) { x * y * z }
puts add.call(3, 4)
puts multiply.call(2, 3, 4)
greet = ->(name = "World") { "Hello, #{name}!" }
puts greet.call
puts greet.call("Ruby")
Proc vs Lambda
my_proc = Proc.new { |x, y| puts "x: #{x}, y: #{y}" }
my_lambda = ->(x, y) { puts "x: #{x}, y: #{y}" }
my_proc.call(1)
def test_return
proc_test = Proc.new { return "proc return" }
lambda_test = -> { return "lambda return" }
proc_test.call
"end"
end
def test_lambda
lambda_test = -> { return "lambda return" }
lambda_test.call
"end"
end
my_proc = Proc.new { }
my_lambda = -> { }
puts my_proc.lambda?
puts my_lambda.lambda?
Closures
def multiplier(factor)
->(x) { x * factor }
end
times_two = multiplier(2)
times_three = multiplier(3)
puts times_two.call(5)
puts times_three.call(5)
def counter
count = 0
increment = -> { count += 1 }
decrement = -> { count -= 1 }
value = -> { count }
[increment, decrement, value]
end
inc, dec, val = counter
inc.call
inc.call
puts val.call
dec.call
puts val.call
Method Objects
class Calculator
def add(x, y)
x + y
end
end
calc = Calculator.new
add_method = calc.method(:add)
puts add_method.call(3, 4)
add_proc = calc.method(:add).to_proc
puts add_proc.call(5, 6)
Symbol to Proc
numbers = [1, 2, 3, 4, 5]
numbers.map { |n| n.to_s }
numbers.map(&:to_s)
["hello", "world"].map(&:upcase)
[1, 2, 3].select(&:even?)
Higher-Order Functions
def compose(f, g)
->(x) { f.call(g.call(x)) }
end
double = ->(x) { x * 2 }
square = ->(x) { x * x }
double_then_square = compose(square, double)
puts double_then_square.call(3)
Currying
add = ->(x) { ->(y) { x + y } }
add_five = add.call(5)
puts add_five.call(3)
multiply = ->(x, y, z) { x * y * z }
curried = multiply.curry
times_two = curried.call(2)
times_two_three = times_two.call(3)
puts times_two_three.call(4)
puts curried.call(2, 3).call(4)
Practical Patterns
Lazy Evaluation
def lazy_value
puts "Computing expensive value..."
42
end
lazy = -> { lazy_value }
puts "Before call"
result = lazy.call
puts result
Callback Pattern
class Button
def initialize
@on_click = []
end
def on_click(&block)
@on_click << block
end
def click
@on_click.each(&:call)
end
end
button = Button.new
button.on_click { puts "Button clicked!" }
button.on_click { puts "Another handler" }
button.click
Strategy Pattern
class Sorter
def initialize(strategy)
@strategy = strategy
end
def sort(array)
@strategy.call(array)
end
end
ascending = ->(arr) { arr.sort }
descending = ->(arr) { arr.sort.reverse }
sorter = Sorter.new(ascending)
puts sorter.sort([3, 1, 2])
sorter = Sorter.new(descending)
puts sorter.sort([3, 1, 2])
Memoization
def memoize(&block)
cache = {}
->(arg) do
cache[arg] ||= block.call(arg)
end
end
expensive_operation = memoize do |n|
puts "Computing for #{n}..."
n * n
end
puts expensive_operation.call(5)
puts expensive_operation.call(5)
Best Practices
- Use blocks for simple iteration and single-use closures
- Use lambdas for strict argument checking and returnable closures
- Use procs for flexible argument handling (rare cases)
- Prefer -> syntax for lambdas (more concise)
- Use &:symbol for simple method calls on collections
- Leverage closures for encapsulation and data privacy
- Use block_given? before yielding to optional blocks
Anti-Patterns
❌ Don't use Proc.new for strict behavior - use lambda instead
❌ Don't ignore return behavior - understand proc vs lambda differences
❌ Don't overuse closures - can lead to memory leaks if not careful
❌ Don't create deeply nested lambdas - hard to read and debug
❌ Don't forget to handle missing blocks - check with block_given?
Related Skills
- ruby-oop - For understanding method context
- ruby-metaprogramming - For dynamic block/proc usage
- ruby-standard-library - For Enumerable methods using blocks