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
Proc
s 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
How to Ssh Interactive Session
Ruby How to Merge Two CSV Files with Slightly Different Headers
How to Set Up the Recipient Id in Public Activity
Ruby Ternary Operator Structure
Scraping a Site That Requires Login Username and Password on Two Separate Pages
Fileutils.Mv Throwing Invalid Char \302 and \255 Exception
Rails 4 Many to Many Association Not Working
Building Gem, Executable Not Found
Fast-Stemmer Installation Problems
Exec the Cd Command in a Ruby Script
Get Pry to Display Characters Like [äöüßÄÖÜß] (Utf-8 Encoding)? (Possibly Windows-Specific Issue)
Best Way to Find_Or_Create_By_Id But Update the Attributes If the Record Is Found
Ruby Converting Utc to User's Time Zone
Ruby: Mass Initializing Instance Variables