I recently purchased the book Seven Languages in Seven Weeks and have been reading through the chapter on Ruby. In the section which introduces blocks (page 40), a code sample is given which illustrates the use of blocks for the purpose of conditionally executing something:
in_case_of_emergency do
use_credit_card
panic
end
def in_case_of_emergency
yield if emergency?
end
This code doesn't make much sense to me, and the book doesn't provide much of an explanation. I was wondering if one of you Ruby gurus would mind helping me get my head around this one.
How can you have both a block and a function with the same name? How would you define "emergency?" I can't even create the block in IRB without it complaining:
NoMethodError: undefined method `in_case_of_emergency' for main:Object
from (irb):1
from :0
And how would you invoke this code to demonstrate how it works? Thanks!
First off: the two are in the wrong order. You need to define in_case_of_emergency first.
Second: You don't name blocks; therefore, it is incorrect that there are two things named in_case_of_emergency. One is a function definition, while the second is the function invocation of that same function.
So, step-by-step:
def emergency?
return rand(2) == 0
end
Let's say you have this function that returns true half the time, and false half the time, randomly. (Boy, that's a lot of emergencies!) Then:
def in_case_of_emergency
yield if emergency?
end
This defines a function named in_case_of_emergency. When called, it executes yield if emergency?, which is a statement yield modified by the conditional if. This is syntactic sugar for
if emergency?()
yield
end
Note that Ruby does not require brackets for function invocation, thus we can drop them; and if there is only one statement inside an if, you can write it on the same line as above (obviating the need for an end).
Next, we have a function invocation:
in_case_of_emergency do
use_credit_card
panic
end
This calls the function we just defined, in_case_of_emergency, passing it a block to execute. These are the two statements (use_credit_card, panic) that would be executed by yield - but only if the emergency? evaluates to true.
Does this make more sense now?
that's simple, that is the method:
def in_case_of_emergency
yield if emergency?
end
and that is the call to your method:
in_case_of_emergency do
use_credit_card
panic
end
wheres
do
use_credit_card
panic
end
is an argument. Ruby methods can implicitly take blocks as arguments. What happens is that
yield will execute the block you provided. In your case yield if emergency? means "execute provided block if conditions are met".
Adding to other answers, it may be easier to understand blocks if you drop the yield keyword and treat blocks in methods like the Procs they actually are.
# for testing purposes, let's always have `emergency?` return `true`
def emergency?
true
end
def in_case_of_emergency(&block)
block.call if emergency?
end
in_case_of_emergency do
puts "AHH! Emergency! Help!", "Seriously, I'm freaking out!"
end
See how much easier it is to realize that the block is actually an argument?
Related
Here is an expectation that utilizes a custom RSpec matcher, yield_variables:
specify { expect{ |p| [3,4,5].my_each(&p) }.to yield_variables [3,4,5] }
my yield_variables matcher's matches? method utilizes a custom class called Probe (Probe is a stripped down version of RSpec's yield probe):
...
def matches? block
ap Probe.probe block
# note i am inspecting what is returned by Probe.probe with ap
end
...
# Probe class is what all my questions are about!
class Probe
attr_accessor :yielded_args
def initialize
self.yielded_args = []
end
def self.probe(block)
probe = new
block.call(probe)
probe.yielded_args
end
def to_proc
Proc.new { |*args| yielded_args << args }
end
end
Now my ap inside matches? reports this: [3,4,5] That is what I expect. However, I have no idea how the Probe class works!!
Problem 1) the matches? block
Normally, the argument we pass to matches? is what we expect the subject to return. i.e, I expect [3,4,5] to be passed into block.
Instead, |p| [3,4,5].my_each(&p) is passed into block, as a proc. Why is this?
Problem 2) block.call(probe)
I'm a bit shakey on procs so please explain slowly. But basically, here we take a new instance of my Probe class and 'send' the block to it, as an argument. That's how I'd explain it to the best of my abilities, but I might have it totally wrong so please explain slowly.
Problem 3) How is to_proc called?
How on earth is .to_proc called automatically? I believe it's called automatically by block.call(probe). Is to_proc an automatically called method like initialize? Is it automatically called whenever the class is sent to a proc? (Btw, the phrase the class is sent to a proc doesn't even make 100% sense to me - please explain. The block isn't passed into the class as an argument anymore. Or if the block is passed as an argument the block.call syntax feels really weird and backwards)
Problem 4) to_proc
How does to_proc have access to the expectation's subject i.e. |p| [3,4,5].my_each(&p) ? How is Proc.new 's *args automatically populated with every single possible yield argument, even though I've only passed in |p| ? How does Proc.new loop along with my my_each, incrementally placing all my args in an array? How does Proc.new have any knowledge of the subject |p| [3,4,5].my_each(&p)? How?? Explain please, and thanks.
Block based marchers work a little differently to other matchers. Typically you want to do something that would not be possible if the expression you were interested in was evaluated and the result passed to you.
For example the raises_error matcher wants to execute the block itself to see that the correct exception is raised and the change matcher wants to evaluate some other expression before and after to see if it changes in the specified way. This is why you are passed a block rather than the value.
The block you are passing to expect takes 1 argument and uses this as the block in the call to my_each, so when your Probe.probe method calls the block it has to pass something as the value. You've phrased as "sending the block to the probe instance" but it is the other way around: the block is called using probe as its argument.
The block executes and calls my_each. Inside here p is the instance of Probe. Prefixing an argument with a & tells ruby that this argument should be used as the method's block (the method being my_each). If the argument is not already a proc ruby calls to_proc on it. This is how Probe#to_proc is called
Assuming that my_each behaves in a similar way to normal each it will call its block (ie the return value of to_proc) once for each of the values in the array.
Normally your matcher would then compare the return value from Probe.probe to the actual value.
I need some help understanding what's going on here. It's a block inside of a method. Basically I get everything here except the call in the if statement wasABlock_nowAProc.call. This is not defined here, so what is it calling?
class Array
def eachEven(&wasABlock_nowAProc)
isEven = true # We start with "true" because arrays start with 0, which is even.
self.each do |object|
if isEven
wasABlock_nowAProc.call object
end
isEven = (not isEven) # Toggle from even to odd, or odd to even.
end
end
end
['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
puts 'Yum! I just love '+fruit+' pies, don\'t you?'
end
# Remember, we are getting the even-numbered elements
# of the array, all of which happen to be odd numbers,
# just because I like to cause problems like that.
[1, 2, 3, 4, 5].eachEven do |oddBall|
puts oddBall.to_s+' is NOT an even number!'
end
def eachEven(&wasABlock_nowAProc) declares that the eachEven method accepts a block, which is the do ... end stuff from your two examples. It is then accessible within the method as a .callable Proc/Lambda object with the name wasABlock_nowAProc.
wasABlock_nowAProc.call object basically invokes the attached block from within the method and passes it one argument, object.
Do a google search for "ruby block to proc" and any of the first results should give you a thorough explanation.
Basically, when you define a method parameter with an & prefix (as in def eachEven(&wasABlock_nowAProc)), it tells Ruby that this method is expecting to receive a Proc object as an argument. In the method body you can then do stuff with Proc, such as use the #call method to run it or pass it on to another method.
Now, it's rare for Ruby programmer to manually create Proc objects. It's much more common to just use a block (less typing, easier to read, etc). If you try to pass a block to method that requires a Proc, well, Ruby handles that just fine. It magically converts the block to a Proc and uses that.
In this particular example, the only reason I can see to define the &wasABlock_nowAProc parameter is to tell Ruby to raise an error if the method is called with a block. You could remove the parameter and replace the #call line with yield to achieve the same functionality.
Because Ruby relies so heavily on the use of blocks for iteration (while and for loops do exist in Ruby, but they're very rarely used by Ruby developers in practice) Ruby allows some keywords often associated with loops in other languages, such as break, next, and redo, to be used inside of blocks. return in Ruby also returns from the method the block is inside, which is consistent with the behavior of a loop, instead of from the block itself as functional programming languages do. This answer explains those behaviors quite nicely.
Now what I'm wondering is how this behavior works from the block caller's perspective (by block caller, I mean the method the block is being called from, as opposed to the method the block is defined in). For some of these keywords it's pretty self-explanatory (e.g. next simply returns from the block, and redo calls the block again with the same arguments), but for others the exact behavior is unclear to me. Specifically...
When I call break from a block, does execution immediately get handed to the point after the block is defined? If I have some post-iteration cleanup steps to perform from within the iterator method, do they get completely skipped? E.g.:
def iterate
setup
1.upto(5) do |x|
yield x
end
cleanup # Does this get called? If not, how can I ensure that it does?
end
iterate do |x|
puts x # Prints 1
break # Break prevents further looping, somehow
end
# Execution continues here after break
What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?
What's even more confusing though is that Ruby blocks can be captured as method parameters and converted to Proc objects. These objects can then be called from anywhere, even outside the method that the block was originally defined in:
def get_proc(&block)
block
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# ...so `break` makes execution continue here...
do_other_stuff
return p
end
p = get_iterator_proc
p.call # ...even though the block isn't actually called until here?
And again, the same goes for return. What does it actually return from in this context? How do these keywords really work?
def iterate
setup
1.upto(5) do |x|
yield x
end
cleanup # Does this get called? If not, how can I ensure that it does?
end
iterate do |x|
puts x # Prints 1
break # Break prevents further looping, somehow
end
# Execution continues here after break
cleanup # Does this get called?
Yes. However, if there is an exception, then exception handling takes precedence.
cleanup # If not, how can I ensure that it does?
You can use ensure without a rescue, for when there is an exception.
break # Break prevents further looping, somehow
I'd suspect it uses throw as that is how Enumerable works.
What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?
It depends on the context. You might get a LocalJumpError: unexpected return or it might work as a break
def get_proc(&block)
block
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# ...so `break` makes execution continue here...
Effectively, but there is a subtle difference. It is more like your first example.
def get_proc(&block)
block
# ...so `break` makes execution continue here...
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# get_proc returned here (after assigning to p)
Remember, get_proc is just a function call with a special syntax to pass functions.
if you had
def get_proc(&block)
block
7
end
Then in get_iterator_proc p would be 7 (And we'd be sad that 7 didn't have a call method )
I'm trying to work on a simple ruby kata. I don't really know the concept of yield so I don't really understand what this error means:
LocalJumpError: yield called out of block
Essentially I'm just trying to make this these test cases pass with this code:
def compute
return yield
"Do not compute"
end
Test.expect(compute { "Block" }, "Block")
Test.expect(compute, "Do not compute")
The first test case passes but the second doesn't. Shouldn't it pass though. The return yield should stop the function early with the block. If the method call does not have a block then it should go straight towards "Do not compute" Right?
You want to use block_given?
def compute
return yield if block_given?
"Do not compute"
end
yield always expects a block to yield to, so you have to prevent the yield from occurring yourself if you don't have a block. Thankfully, there's the inbuilt method, block_given? that can tell you whether there's been a block provided or not.
Try removing the return statement.
Once you call return, you exits the method, so the "Do not compute" statement is never reached.
I am using ruby 1.8.7.
p = lambda { return 10;}
def lab(block)
puts 'before'
puts block.call
puts 'after'
end
lab p
Above code output is
before
10
after
I refactored same code into this
def lab(&block)
puts 'before'
puts block.call
puts 'after'
end
lab { return 10; }
Now I am getting LocalJumpError: unexpected return.
To me both the code are doing same thing. Yes in the first case I am passing a proc and in the second case I am passing a block. But &block converts that block into proc. So proc.call should behave same.
And yes I have seen this post Using 'return' in a Ruby block
When you pass in the block with &, you're converting it to a proc. The important point is that a proc and a lambda are different (lambda is actually a subclass of proc), specifically in how they deal with return.
So your refactored code is actually the equivalent of:
p = Proc.new { return 10;}
def lab(block)
puts 'before'
puts block.call
puts 'after'
end
lab p
which also generates a LocalJumpError.
Here's why: A proc's return returns from its lexical scope, but a lambda returns to its execution scope. So whereas the lambda returns to lab, the proc passed into it returns to the outer scope in which it was declared. The local jump error means it has nowhere to go, because there's no enclosing function.
The Ruby Programming Language says it best:
Procs have block-like behavior and lambdas have method-like behavior
You just have to keep track of what you're using where. As others have suggested, all you need to do is drop the return from your block, and things will work as intended.
return inside a block will return from the method the block is in, not from the block. To return from the block use next (it's named that way because with iterator-methods like each and map returning from the block basically means jumping to the next iteration of the loop).
Note that when the return value is the last evaluated expression in the block, you don't need any kind of return statement at all, i.e. lab { 10 } will do the same thing.
The {} block includes the context in which it is given, so the return tries to return from the line lab { return 10; }. You can actually make this work (sometimes even in a useful manner) by placing that line inside a method, which will then return (i.e. "after" is not printed).
To return the 10 to block.call, omit the return (or substitute next).
I think you just need to dereference the block before you pass it:
foo = lambda { return 10 }
def trace_block(&fn)
puts 'before calling fn'
puts fn.call
puts 'after caling fn'
end
trace_block(&foo)
Output:
before calling fn
10
after caling fn
More info:
Understanding Ruby Blocks, Procs and Lambdas
Ruby Blocks 101