Callable objects power callbacks, strategies, Rails scopes and every DSL you'll meet.
A block is not an object, but you can capture one. Declaring &block as the last parameter turns the caller's block into a Proc object you can store and call later with block.call(x) or block.(x). The reverse, &proc at a call site, passes a Proc as a block.
Create callables directly:
square = ->(x) { x * x }is a lambda (thelambda { |x| }form also works).Proc.new { |x| ... }orproc { |x| ... }creates a plain proc.
They differ in two ways:
- Arity: lambdas check the number of arguments like methods do; procs are lenient, filling missing args with nil and dropping extras.
- return: in a lambda,
returnexits the lambda. In a proc,returnreturns from the method where the proc was defined, and raisesLocalJumpErrorif that method has already finished.
So lambdas behave like anonymous methods and are the usual choice for stored callables, such as Rails scope :active, -> { where(active: true) }.
All of them are closures: they keep access to the local variables in scope where they were created, even after that scope has returned. That's how counters and configuration callbacks work.
obj.method(:name) returns a Method object, and & converts it too: %w[1 2].map(&method(:Integer)). Callables compose with >> and <<, and curry creates partially applied versions.
Example
square = ->(x) { x * x }
inc = proc { |x| (x || 0) + 1 }
p square.(4) # 16
p inc.call # 1 (procs are lenient about args)
begin
square.call
rescue ArgumentError => e
puts e.message # wrong number of arguments (given 0, expected 1)
end
def make_counter
count = 0
-> { count += 1 } # closes over count
end
c = make_counter
c.call; c.call
p c.call # 3
def run(&block) = block.call(10)
p run { it / 2 } # 5
p %w[1 2 3].map(&method(:Integer)) # [1, 2, 3]
pipeline = square >> inc >> :to_s.to_proc
p pipeline.call(3) # "10"
p [square.lambda?, inc.lambda?] # [true, false]Remember
- &block captures a block as a Proc; &callable passes one as a block
- Lambdas check arity and return locally; procs do neither
- All are closures over the variables where they were created
Watch out for
- Using return inside a proc that outlives its method, causing LocalJumpError.