Reputation: 1816
I'm looking into blocks at the moment, and they have stumped me.
I used this as an example:
class ProcExample
attr_reader :proc_class
def initialize(&block)
@stored_proc = block
@proc_class = @stored_proc.class
end
def use_proc(arg)
@stored_proc.call(arg)
end
end
eg = ProcExample.new {|t| puts t}
p eg.proc_class
p eg.use_proc("Whoooooooo")
Now I kind of (not really( understand how the block is passed into @stored_proc. I used @proc_class because I was curious what class the block object was actually stored as.
But what if I wanted to store a block in a regular variable?
E.g.:
block_object = {|param| puts param**2}
But I found that this is treated as a Hash and not a block/proc. Naturally an error arises. I've tried assigning it with an ampersand in the variable name, and at the beginning of the block but that doesn't work.
Eventually I was wondering if it was possible to call a function and replace the block with a variable containing the block.
Like so:
(1..10).each block_object
Is this possible in Ruby?
Upvotes: 0
Views: 1051
Reputation: 369624
You cannot assign blocks to a variable.
Blocks aren't really objects. They are special syntax for passing code to a higher-order method. If you want a piece of executable code that you can assign to a variable, pass around and manipulate, you need to use a Proc
object.
There are two kinds of Proc
s: lambdas and regular procs. They behave differently in two aspects: argument binding semantics and return
semantics. lambdas bind arguments like methods and return
returns from the lambda, just like return
in a method returns from the method. Regular procs bind arguments like blocks and return
returns from the enclosing method, not the proc, just like return
in a block.
Regular procs can be created by passing a block to Proc.new
or alternatively to Kernel#proc
. Lambdas can be created by passing a block to Kernel#lambda
or with the "stabby lambda" literal syntax:
lambda_object = ->param { puts param**2 }
In order to convert Proc
s to blocks and the other way around, Ruby has the unary prefix &
modifier. This modifier is only valid in parameter lists and argument lists. When used in a parameter list, it means "wrap the block in a proc and bind it to this variable". When used in an argument list. it means "unwrap this proc into a block (and if it's not a proc already, call to_proc
on it first) and pass it as a block argument".
(1..10).each(&lambda_object)
I'm surprised that you haven't already seen the unary prefix &
modifier used in this way, it is actually fairly common, e.g. in something like ['1', '2'].map(&:to_s)
.
Another kind of object that also represents a piece of executable code is a Method
object. It supports some of the same interface as Proc
s do, in particular #call
, #to_proc
, #arguments
, #arity
etc. There are two ways to get a Method
object: either grab a method that is bound to a receiver from that receiver using the Object#method
method or grab an UnboundMethod
object from a class or module (e.g. using Module#instance_method
) and bind it to a receiver using UnboundMethod#bind
which will return a Method
object.
Since Method
implements to_proc
, you can pass it to a method as a block using the unary prefix &
modifier, e.g.:
# Warning: extremely silly example :-)
ary = []
(1..10).each(&ary.method(:<<))
ary
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ary = []
(1..10).each(&Array.instance_method(:<<).bind(ary))
ary
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Upvotes: 5
Reputation: 6971
You can use a proc or lambda. There are some subtle differences between them; and between Ruby versions. A good overview can been seen here: https://www.youtube.com/watch?v=VBC-G6hahWA given by Peter Cooper
Upvotes: 1
Reputation: 5993
You are looking for a proc object, I believe.
block = proc { ... }
Upvotes: 2