Question

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?

Was it helpful?

Solution

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 Procs: 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 Procs 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 Procs 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]

OTHER TIPS

You are looking for a proc object, I believe.

block = proc { ... }

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top