Yielding in an Anonymous Block

Yielding in an anonymous block

The lambda is a closure and it seems to be capturing the block_given? and block from its outer scope. This behavior does make sense as the block is, more or less, an implied argument to the outer method; you can even capture the block in a named argument if desired:

def def_test(&block)
frobnicate &block
end

So the block is part of the argument list even when it isn't named.

Consider this simple piece of code:

def f
lambda do
puts "\tbefore block"
yield if block_given?
puts "\tafter block"
end
end

puts 'Calling f w/o block'
x = f; x.call
puts

puts 'Calling f w/ block'
x = f { puts "\t\tf-block" }; x.call
puts

puts 'Calling f w/o block but x with block'
x = f; x.call { puts "\t\tx-block" }
puts

puts 'Calling f w/ block and x with block'
x = f { puts "\t\tf-block" }; x.call { puts "\t\tx-block" }

This produces the following for me with 1.9.2:

Calling f w/o block
before block
after block

Calling f w/ block
before block
f-block
after block

Calling f w/o block but x with block
before block
after block

Calling f w/ block and x with block
before block
f-block
after block

Furthermore, Proc#call (AKA proc ===) doesn't take a block:

prc === obj → result_of_proc
Invokes the block, with obj as the block‘s parameter. It is to allow a proc object to be a target of when clause in the case statement.

Compare the first line with the documentation for Enumerable#chunk (for example):

enum.chunk {|elt| ... } → an_enumerator

The {...} indicates that chunk is documented to take a block, the lack of such notation for Proc#call indicates that Proc#call does not take a block.

This isn't exactly an authoritative answer but maybe it clears things up a little bit.

yield to an anonymous block two functions up

I think maybe the concept of yield will be more clear if you look at an alternative syntax, which is converting the bloc to a proc argument.

For example, the following examples are the same

def my_each(arr)
arr.each { |x| yield x }
end

def my_each(arr, &blk)
arr.each { |x| blk.call(x) }
end

# Both are called the same way
my_each([1,2,3]) { |x| print x }
# => 123

When using yield, the variable is available in the method without declaring it in the parameters list. Prepending an & sign to a parameter converts it to a proc, so in the method it can be run with .call.

Here's an example of providing a block to one method then executing it two scopes in:

def method_a(number, &blk)
method_b do
method_c do
blk.call(number)
end
end
end

def method_b(&blk)
blk.call
end

def method_c(&blk)
blk.call
end

method_a(1) { |num| puts num + 1 }
# => 2

Note that blk is not a magic word - you can name the variable whatever you want.

Here's the same thing with yield:

def method_a(number)
method_b do
method_c do
yield number
end
end
end

def method_b
yield
end

def method_c
yield
end

method_a(1) { |num| puts num + 1 }
# => 2

I think using the &blk syntax is clearer because it assigns a variable to the proc. Just because a proc is used in the method doesn't mean you have to ever run Proc.new. The block is automatically converted to a proc.

In C#, why can't an anonymous method contain a yield statement?

Eric Lippert recently wrote a series of blog posts about why yield is not allowed in some cases.

  • Part 1
  • Part 2
  • Part 3
  • Part 4
  • Part 5
  • Part 6

EDIT2:

  • Part 7 (this one was posted later and specifically addresses this question)

You will probably find the answer there...


EDIT1: this is explained in the comments of Part 5, in Eric's answer to Abhijeet Patel's comment:

Q :

Eric,

Can you also provide some insight into
why "yields" are not allowed inside an
anonymous method or lambda expression

A :

Good question. I would love to have
anonymous iterator blocks. It would be
totally awesome to be able to build
yourself a little sequence generator
in-place that closed over local
variables. The reason why not is
straightforward: the benefits don't
outweigh the costs. The awesomeness of
making sequence generators in-place is
actually pretty small in the grand
scheme of things and nominal methods
do the job well enough in most
scenarios. So the benefits are not
that compelling.

The costs are large. Iterator
rewriting is the most complicated
transformation in the compiler, and
anonymous method rewriting is the
second most complicated. Anonymous
methods can be inside other anonymous
methods, and anonymous methods can be
inside iterator blocks. Therefore,
what we do is first we rewrite all
anonymous methods so that they become
methods of a closure class. This is
the second-last thing the compiler
does before emitting IL for a method.
Once that step is done, the iterator
rewriter can assume that there are no
anonymous methods in the iterator
block; they've all be rewritten
already. Therefore the iterator
rewriter can just concentrate on
rewriting the iterator, without
worrying that there might be an
unrealized anonymous method in there.

Also, iterator blocks never "nest",
unlike anonymous methods. The iterator
rewriter can assume that all iterator
blocks are "top level".

If anonymous methods are allowed to
contain iterator blocks, then both
those assumptions go out the window.
You can have an iterator block that
contains an anonymous method that
contains an anonymous method that
contains an iterator block that
contains an anonymous method, and...
yuck. Now we have to write a rewriting
pass that can handle nested iterator
blocks and nested anonymous methods at
the same time, merging our two most
complicated algorithms into one far
more complicated algorithm. It would
be really hard to design, implement,
and test. We are smart enough to do
so, I'm sure. We've got a smart team
here. But we don't want to take on
that large burden for a "nice to have
but not necessary" feature. -- Eric

How to yield return inside anonymous methods?

Ok so I did something like this which does what I wanted (some variables omitted):

public static void Run ( Action<float, EffectResult> action )
{
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
var result = image.Apply (effect);

action (100 * ( index / count ), result );
}
}
};

and then in the call site:

GlobalGraph.Run ( ( p, r ) =>
{
this.Progress = p;
this.EffectResults.Add ( r );
} );

string concatenation and yielding block based on if modifier

You can't concatenate strings like that. The line yield(block) + isn't a complete line. That's why you're getting the errors. Here are two possible fixes:

def wrapper(form, attr, options = {}, &block)
if block_given?
return yield(block) + form.label(form_label, class: "control-label")
end
form.label(form_label, class: "control-label")
end

Or this

def wrapper(form, attr, options = {}, &block)
content = ''
if block_given?
content = yield(block)
end
content + form.label(form_label, class: "control-label")
end

Ruby: yield block from a block?

I'm not sure if you can you can do that, but something similar would be:

In Ruby 1.8.6:

a = lambda { |my_proc|
puts 'in a'
my_proc.call
}

a.call(lambda { puts "in a's block" })

In Ruby 1.9.1, you can have block parameters

a = lambda { |&block|
puts 'in a'
block.call
}

a.call { puts "in a's block" }

Ruby: the yield inside of a block

The block is passed similarly to the argument of that function. This can be specified explicitly, like so:

class Test
def my_each(&block)
"abcdeabcabc".scan("a") do |x|
puts "!!! block"
yield x
# Could be replaced with: block.call(x)
end
end
end

Technically, it's exactly the same (puts put in there for clarification), its presence is not checked the way it is usually done for arguments. Should you forget to give it a block, the function will halt on the first yield it has to execute with exactly the same LocalJumpError (at least, that's what I get on Rubinius). However, notice the "!!! block" in the console before it happens.

It works like that for a reason. You could check whether your function is given a block, if it is specified explicitly as above, using if block, and then skip the yields. A good example of that is a content_tag helper for Rails. Calls of this helper can be block-nested. A simplistic example:

content_tag :div do
content_tag :div
end

...to produce output like:

<div>
<div></div>
</div>

So, the block is executed "on top" (in terms of call stack) of your method. It is called each time a yield happens as some sort of function call on a block. It's not accumulated anywhere to execute the block afterwards.

UPD:

The Enumerator returned by many eaches is explicitly constructed by many iterators to save context of what should happen.

It could be implemented like this on my_each:

class Test
def my_each(&block)
if block
"abcdeabcabc".scan("a") { |x| yield x }
else
Enumerator.new(self, :my_each)
end
end
end


Related Topics



Leave a reply



Submit