Blocks & Procs in Ruby

Can I pass a block to a Proc?

Procs can't accept blocks as implicit arguments (the format you're trying). A proc can receive other proc objects as arguments, either explicitly, or using & arguments. Example:

a = Proc.new do |&block|
block.call
end

a.call() {puts "hi"}

yield is a bit of laguage level magic that only works in the context of a method.

Blocks & Procs in Ruby

There are a lot of things that are good about blocks. The elevator pitch: Blocks let us pass around actions the same way we normally pass around data.

The most obvious level is that they let you abstract things out into functions that wouldn't be possible otherwise. For example, let's look at a common case where you have a list of things and you want to filter it to only include items that match some criterion:

int list[50] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
int evenNumbers[50] = {0};
int copyIndex = 0;
for (int i = 0; i < 50; i++) {
if (list[i] % 2 == 0) {
evenNumbers[copyIndex++] = list[i];
}
}

Here's how you write that in Ruby:

list = 1..50
listCopy = list.select {|n| n.even?}

All the common busywork is moved out of your code and into a method with a meaningful name. We don't care about copying the array and going through indexes and all that — we just want a filtered list. And that's what select gives us. The block allows us to pass our custom logic into this standard method.

But iterators aren't the only place where this "hole in the middle pattern" is useful. For example, if you pass a block to File.open, it will open the file, execute the block with the file and then close the file for you.

Another thing that blocks give us is a really powerful form of callbacks. For example, without blocks, we might have to do something like this (based on how dialogs actually work in Objective-C Cocoa):

class Controller
def delete_button_clicked(item)
item.add_red_highlight
context = {:item => item}
dialog = Dialog.new("Are you sure you want to delete #{item}?")
dialog.ok_callback = :delete_OK
dialog.ok_receiver = self
dialog.cancel_callback = :cancel_delete
dialog.cancel_receiver = self
dialog.context = context
dialog.ask_for_confirmation
end

def delete_OK(sender)
delete(sender.context[:item])
sender.dismiss
end

def cancel_delete(sender)
sender.context[:item].remove_red_highlight
sender.dismiss
end
end

Yowza. With blocks, we could do this instead (based on a common pattern used in many Ruby libraries):

class Controller
def delete_button_clicked(item)
item.add_red_highlight
Dialog.ask_for_confirmation("Are you sure you want to delete #{item}?") do |response|
response.ok { delete item }
response.cancel { item.remove_red_highlight }
end
end
end

That's actually two levels of blocks — the do...end block and the two {}-style blocks. But it reads pretty naturally, doesn't it? This works because a block captures the context it's created in, so we don't need to pass around self and item.

As for Procs, they're just an object wrapper for blocks. Not very much to them.

Ruby blocks and procs. Do you not need to specify a second argument when using a Proc?

All methods always take exactly one optional block argument:

puts('Hello') { I.can.write.whatever.i.want.here.because.the.block.is.never.ran }
# Hello

def foo; end
foo { same.here }

The whole point of blocks is that they are syntactically and semantically lightweight, and one of the ways this is achieved is that they are anonymous:

def bar; yield if block_given? end
# Note how I didn't need to say where to yield to and what block to check since
# there can only ever be at most one anyway.

bar { puts 'Hello' }
# Hello

They are also not objects.

But if it doesn't have a name and isn't even an object, then how can you refer to it? Well, that's where & parameters come in: they tell Ruby to convert the block to a proper Proc object and bind it to a parameter:

def baz(&blk) p blk end
baz { again.we.are.not.calling.the.block }
# #<Proc:0xdeadbeef081542@(pry):42>
# ^^^^^^^ Debugging representation of a Proc object

Procs have a call method which executes them (there's also a piece of syntactic sugar for calling call):

def quux(grault, &blk)
blk.(grault)
yield grault
end

quux('Hello') {|garply| puts garply }
# Hello
# Hello

Why do Ruby procs/blocks with splat arguments behave differently than methods and lambdas?

There are two types of Proc objects: lambda which handles argument list in the same way as a normal method, and proc which use "tricks" (Proc#lambda?). proc will splat an array if it's the only argument, ignore extra arguments, assign nil to missing ones. You can partially mimic proc behavior with lambda using destructuring:

->((x, y)) { [x, y] }[1]         #=> [1, nil]
->((x, y)) { [x, y] }[[1, 2]] #=> [1, 2]
->((x, y)) { [x, y] }[[1, 2, 3]] #=> [1, 2]
->((x, y)) { [x, y] }[1, 2] #=> ArgumentError

How can I pass a block/proc and variable directly to a function?

You seem to be confusing blocks and procs. A block is not a ruby object. You cannot pass it like

foo(args, block)

The only way to pass it is foo(args){...} or foo(args) do ... end. However, in method definition, you can receive a block, convert it into a proc, and accept it as one of its arguments like this:

def foo(args, &block)
...
end

On the other hand, You can pass a proc object as an argument like

foo(args, proc)

There are several ways to create a proc object, and a literal that is close to the block literal is the lambda: ->{...}, but it is different from the block syntax {...}. Using this, you can do

foo(args, ->{...})

but not

foo(args, {...})

unless {...} is a hash.

ruby block/procs and method call

The block following your method call { |s| puts s } does nothing until you tell it to. If you continue reading the page they explain blocks further down - here is an example:

def accepts_hash( var )
print "got: ", var.inspect # will print out what it received
yield ' jenny from the block' # pass value back to block
end

accepts_hash( { :arg1 => 'giving arg1', :argN => 'giving argN' } ) { |s| puts s }

=> {:arg1=>"giving arg1", :argN=>"giving argN"} jenny from the block

By yielding, we can return and process the block - in this case s represents the string we are yielding, and ' jenny from the block' is its value.

Blocks make ruby more flexible and declarative, allowing you to write idiomatic and human readable code. For example:

3.times { p 'hello' } 
=> "hello"
=> "hello"
=> "hello"

ruby is a gorgeous lanuage - more info on blocks and practical usage: http://www.gotealeaf.com/blog/declarative-thinking-with-higher-order-functions-and-blocks

Return statements inside procs, lambdas, and blocks

As one answer in the linked question shows:

The return keyword always returns from the method or lambda in the current context. In blocks, it will return from the method in which the closure was defined. It cannot be made to return from the calling method or lambda.

Your first example was successful because you defined victor in the same function you wanted to return from, so a return was legal in that context. In your second example, victor was defined in the top-level. The effect of that return, then, would not be to return from batman_yield (the calling method), but [if it were valid] to return from the top-level itself (where the Proc was defined).

Clarification: while you can access the return value of a block (i.e. "The value of the last expression evaluated in the block is passed back to the method as the value of the yield" - as per your comment), you can't use the return keyword, for the reason stated above. Example:

def batman_yield
value = yield
return value
"Iron man will win!"
end

victor = Proc.new { return "Batman will win!" }
victor2 = Proc.new { "Batman will win!" }

#batman_yield(&victor) === This code throws an error.
puts batman_yield(&victor2) # This code works fine.

Ruby Blocks, Procs and Local Variables

The Proc.new call creates a closure for the block that it's given. In creating a closure for the block, the block is bound to the original variables in the scope of the Proc.new call.

Why is this done?

It allows Ruby blocks to function as closures. Closures are extremely useful, and the Wikipedia entry (linked above) does an excellent job of explaining some of their applications.

How is this done?

This is done in the Ruby VM (in C code) by copying the Ruby control frame that exists before entering the Proc.new method. The block is then run in the context of this control frame. This effectively copies all of the bindings that are present in this frame. In Ruby 1.8, you can find the code for this in the proc_alloc function in eval.c. In Ruby 1.9, you can find this in the proc_new function in proc.c.



Related Topics



Leave a reply



Submit