I understand that we use the ampersand in front of the method's argument in order to avoid using yield, which would output an error if it doesn't find a block in its way.
However, I've seen that sometimes we use the & in front of the argumenent when we call a method, in order to transform the proc back into a block. It eventually happens that there are 2 ampersands, one for turning blocks into procs, and the second for the opposite.
How's that?! When do we need to put the ampersand to getting back a block from a proc, and why?
I hope I've been clear, beacause not being english mother tongue could make harsh to pose difficult questions for me.
Example
I took this example from the slides of CodeSchool, which can be found on http://courseware.codeschool.com/ruby_bits_2/ruby_bits_2_level_1.pdf.
class Timeline
attr_accessor :tweets
def each(&block)
tweets.each(&block)
end
end
I understand that we use the ampersand in front of the method's
argument in order to avoid using yield, which would output an error if
it doesn't find a block in its way.
Your understanding is wrong.
You use &block as the last argument to capture the block into a Proc, in particular to store it and use it later on. When calling a method, &block does the reverse, i.e. specifies that the block corresponds to the given block. This is the same idea as def foo(*args) and foo(*args).
The example you gave is basically equivalent to:
def each
tweets.each{|t| yield y}
end
As Yehuda Katz explains at the end of this talk, you should think of a "block" as a Proc with a special argument position and a dedicated syntax to specify it.
It's common to use them together. This is the case because blocks can't be used as parameters, while procs can. So if you need to use the block as a parameter within the method's body, you must turn it into a proc.
Within that method, you might want to pass the proc to another method that only accepts a block, so you have to do the conversion once again.
For example:
def each(&block) # block to proc
posts.each(&block) # proc to block
end
Related
This is somewhat of an esoteric situation that I am not even sure is possible. I have a method that is called by a lot of methods:
def called_by_a_lot_of_methods
# Work
end
I am introducing a feature in which the called_by_a_lot_of_methods will do stuff depends on the caller method, and my current solution is to change all these methods to pass their names when calling called_by_a_lot_of_methods:
def some_method
called_by_a_lot_of_methods(method_name: some_method)
end
Or, alternatively:
def some_method
called_by_a_lot_of_methods(method_name: __method__)
end
But this is becoming tedious, and I was wondering if I can give the method_name parameter a default value:
def called_by_a_lot_of_methods(method_name: __method__)
# Work
end
This does not work because __method__ evaluates immediately giving called_by_a_lot_of_methods, which is obviously not what I want. The question then is, is there a way in Ruby to defer the evaluation of the argument until a useful time when I know it should give the correct result, which is the outer caller method? Thus saving me from having to pass the argument everywhere?
I'm trying to understand when one should code blocks implicitly or explicitly. Given the following code blocks:
Implicit
def two_times_implicit
return "No block" unless block_given?
yield
yield
end
puts two_times_implicit { print "Hello "}
puts two_times_implicit
Explicit
def two_times_explicit (&i_am_a_block)
return "No block" if i_am_a_block.nil?
i_am_a_block.call
i_am_a_block.call
end
puts two_times_explicit { puts "Hello"}
puts two_times_explicit
Is it preferable to code using one over the other? Is there a standard practice and are there instances where one would work better or differently than the other and where one would not work at all?
Receiving a block via & creates a new proc object out of the block, so from the point of view of efficiency, it is better not to use it. However, using & generally makes it easier to define methods that may or may not take a block, and using &, you can also handle blocks together with arguments, so it is preferred by many.
Actually, according to one very interesting read, second variant is 439% slower (related thread on HackerNews).
TL;DR: Creating and passing a block via yield is a highly optimized common case in MRI, which is handled by dedicated C function in interpreter, while passing &block is implemented differently and has a big overhead of creating new environment and creating Proc itself on every call.
Summing up, use &block only if you need passing it further (for example, to a next function), or manipulate it somehow in other way. Otherwise, use yield, since it's way faster.
Ruby Koans has the following exercise in about_blocks.rb:
def method_with_block_arguments
yield("Jim")
end
def test_blocks_can_take_arguments
method_with_block_arguments do |argument|
assert_equal __, argument
end
end
I know the answer is assert_equal "Jim", argument, but I'm struggling to understand what is happening. Specifically:
Is argument or assert_equal... the block?
What is yield doing given that method_with_block_arguments returns "Jim" without yield?
I think some of the above commenters are correct in saying that you currently don't have a very deep understanding of Ruby, but don't let that discourage you. It just takes time to learn. When I was first learning Ruby, the concept of blocks and their syntax did take some time to wrap my head around. Once you get it the syntax is very simple, but you until you reach that point...
Anywho, this is my attempt to help you out. :)
argument is a block variable. All the stuff between do and end is the block. assert_equal is just a regular method call, nothing to do with blocks.
What yield does is the key to understanding how blocks work. What yield does it that it "yields" control to the calling function. You may think of it as a callback. When you say "yield" in the middle of a function, you are essentially saying "in the middle of this function, I want to allow someone else to plug in their code and make decisions about what should happen." If you use yield with no arguments, no data from your method gets passed back to the caller.
In essence, yield is a way of "yielding" control to somebody else, in this case the caller of your function.
When you call yield with one or more arguments, you are passing data from the your function back up to the caller. So when you say yield("Jim") you are handing the String "Jim" back to whoever calls method_with_block_arguments.
Lastly, you have to understand that in Ruby, methods always return the result of whatever was the last expression in a particular method. That's why you usually don't need an explicit return statement.
For instance, this method will return 42.
def foo
42
end
That's because 42 is a valid expression in Ruby. It's just an identity, but it's valid Ruby, so Ruby just says "okay, you said 42 and that's the last thing in this method declaration. So when people call 'foo' they get 42 back".
I hope this helps. I think at this point you should assume that you're still pretty early on in terms of your Ruby learning, but you're on the right track investigating blocks. Once you get them you'll understand one of the most powerful parts of Ruby.
Is argument or assert_equal... the block?
No, neither argument nor assert_equal is a block, argument is the variable and anything between do and end is the block. assert_equal is a normal method call.
What is yield doing given that method_with_block_arguments returns "Jim" without yield?
Yield is what makes it special. It calls the block (ie. everything between do and end) and executes it. "Jim" is the argument to the block.
Here is a gist that I copied from Paul while I was learning ruby. That should help in learning about closures in ruby.
I'm writing a simple dsl in ruby. Few weeks ago I stumbled upon some blog post, which show how to transform code like:
some_method argument do |book|
book.some_method_on_book
book.some_other_method_on_book :with => argument
end
into cleaner code:
some_method argument do
some_method_on_book
some_other_method_on_book :with => argument
end
I can't remember how to do this and I'm not sure about downsides but cleaner syntax is tempting. Does anyone have a clue about this transformation?
def some_method argument, &blk
#...
book.instance_eval &blk
#...
end
UPDATE: However, that omits book but don't let you use the argument. To use it transparently you must transport it someway. I suggest to do it on book itself:
class Book
attr_accessor :argument
end
def some_method argument, &blk
#...
book.argument = argument
book.instance_eval &blk
#...
end
some_method 'argument' do
some_method_on_book
some_other_method_on_book argument
end
Take a look at this article http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation — there is an overview of the method (specifically stated in the context of its downsides and possible solution to them), plus there're several useful links for further reading.
Basically, it's about using instance_eval to execute the block in the desirable context.
Speaking about downside of this technique:
So what's the problem with it? Well, the problem is that blocks are
generally closures. And you expect them to actually be full closures.
And it's not obvious from the point where you write the block that
that block might not be a full closure. That's what happens when you
use instance_eval: you reset the self of that block into something
else - this means that the block is still a closure over all local
variables outside the block, but NOT for method calls. I don't even
know if constant lookup is changed or not.
Using instance_eval changes the rules for the language in a way that
is not obvious when reading a block. You need to think an extra step
to figure out exactly why a method call that you can lexically see
around the block can actually not be called from inside of the block.
Check out the docile gem. It takes care of all the sharp edges, making this very easy for you.
I am new to Ruby and am learning from reading an already written code.
I encounter this code:
label = TkLabel.new(#root) do
text 'Current Score: '
background 'lightblue'
end
What is the semantics of the syntax "do" above?
I played around with it and it seems like creating a TkLabel object then set its class variable text and background to be what specified in quote. However when I tried to do the same thing to a class I created, that didn't work.
Oh yeah, also about passing hash into function, such as
object.function('argument1'=>123, 'argument2'=>321)
How do I make a function that accepts that kind of argument?
Thanks in advance
What you're looking at is commonly referred to as a DSL, or Domain Specific Language.
At first glance it may not be clear why the code you see works, as text and background are seemingly undefined, but the trick here is that that code is actually evaluated in a scope in which they are. At it's simplest, the code driving it might look something like this:
class TkLabel
def initialize(root, &block)
#root = root
if block
# the code inside the block in your app is actually
# evaluated in the scope of the new instance of TkLabel
instance_eval(&block)
end
end
def text(value)
# set the text
end
def background(value)
# set the background
end
end
Second question first: that's just a hash. Create a function that accepts a single argument, and treat it like a hash.
The "semantics" are that initialize accepts a block (the do...end bit), and some methods accepting string parameters to set specific attributes.
Without knowing how you tried to do it, it's difficult to go much beyond that. Here are a few, possible, references that might help you over some initial hurdles.
Ruby is pretty decent at making miniature, internal DSLs because of its ability to accepts blocks and its forgiving (if arcane at times) syntax.