yield or call?

R

Ronald Fischer

def f(&b)
...
b.call
...
end

def g
...
yield b
...
end

As far I understood the documentation for call and yield, both are
equivalent:

f { ... }
g { ... }

Is there a principal reason when to prefer call over yield (or vice
versa), or are these only syntactic variations of the same feature?

Ronald
--=20
Ronald Fischer <[email protected]>
Phone: +49-89-452133-162
 
P

Phlip

Ronald said:
Is there a principal reason when to prefer call over yield (or vice
versa), or are these only syntactic variations of the same feature?

I am learning to consider 'yield' as a fossil of an early Ruby that could
not treat the bound block as a variable. I only use 'yield' as a slight
convenience - a few less characters to type - in application-specific code.

When writing API-style code, I always start with &block because it's only a
matter of time before I refactor the code and start passing that &block into
a helper method.
 
J

Jano Svitok

There is a difference in object allocation. Ruby allocates 1 object for
each function call, one more in the case of the &b form (it has to
allocate the Proc).

Then, yield costs only one object while Proc#call costs two. It can make a
difference in case of large iterations, if you're concerned (like I am)
about the GC eating your time.

Sylvain
 
D

dblack

Hi --

def f(&b)
...
b.call
...
end

def g
...
yield b
...
end

As far I understood the documentation for call and yield, both are
equivalent:

f { ... }
g { ... }

Is there a principal reason when to prefer call over yield (or vice
versa), or are these only syntactic variations of the same feature?

I think you mean just yield (not yield b).

yield is a lot faster:

david-a-blacks-computer:~/hacking dblack$ ruby yi.rb

require 'benchmark'
include Benchmark

def x(&b)
b.call
end

def y
yield
end


n = 1000000

bmbm do |b|
b.report("b") { n.times { x {} } }
b.report("y") { n.times { y {} } }
end

Rehearsal -------------------------------------
b 3.760000 0.010000 3.770000 ( 3.770292)
y 0.590000 0.000000 0.590000 ( 0.595891)
---------------------------- total: 4.360000sec

user system total real
b 3.780000 0.000000 3.780000 ( 3.784505)
y 0.610000 0.000000 0.610000 ( 0.609034)

So I'd only use the &b/call version if you really need the thing as an
object. I also consider yield more idiomatic, or something... since
the whole anatomy of the method call is designed to provide the extra
component (the code block). When you capture the block as a proc,
you're capturing a syntactic construct as an object -- sort of like:

def x %arg_list # imaginary example

where arg_list was an ArgumentList object. That's logically secondary
to the presence of the argument list in the first place, and similarly
the block exists prior to, and independently of, the fact that the
language lets you capture it in a variable.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
D

dblack

Hi --

I am learning to consider 'yield' as a fossil of an early Ruby that could not
treat the bound block as a variable. I only use 'yield' as a slight
convenience - a few less characters to type - in application-specific code.

When writing API-style code, I always start with &block because it's only a
matter of time before I refactor the code and start passing that &block into
a helper method.

I think that's premature pessimization :) See my other post for some
benchmarks on yield vs. capture-and-call.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
R

Ronald Fischer

Is there a principal reason when to prefer call over yield (or vice
=20
I think you mean just yield (not yield b).
Correct!

yield is a lot faster:

Thank you (and also to all the others) for the clarification.

Ronald
 
R

Robert Dober

I am learning to consider 'yield' as a fossil of an early Ruby that could
not treat the bound block as a variable. I only use 'yield' as a slight
convenience - a few less characters to type - in application-specific code.
Do not do this, yield suffers a lot from a bad reputation it does not
deserve, see also David's benchmarks above.
When writing API-style code, I always start with &block because it's only a
matter of time before I refactor the code and start passing that &block into
a helper method.
Why? I do not think that this is necessary.
Just replace the refactoring
old: yield ==> something &blk
 
R

Robert Dober

I am learning to consider 'yield' as a fossil of an early Ruby that could
not treat the bound block as a variable. I only use 'yield' as a slight
convenience - a few less characters to type - in application-specific code.
Do not do this, yield suffers a lot from a bad reputation it does not
deserve, see also David's benchmarks above.
When writing API-style code, I always start with &block because it's only a
matter of time before I refactor the code and start passing that &block into
a helper method.
Why? I do not think that this is necessary.
Just replace the refactoring
old: yield ==> something &blk + changing the def
with
new: yield ==> something &Proc.new

However I *never* use yield, but for a completely different reason, I
want my def to show if I yield or not.
In case I am unhappy with performance I can still change back.

Cheers
Robert
 
G

Gregory Brown

Is there a principal reason when to prefer call over yield (or vice
versa), or are these only syntactic variations of the same feature?

In addition to what others have said (especially David Black), they
throw different errors.
LocalJumpError: no block given
from (irb):2:in `foo'
from (irb):4NoMethodError: undefined method `call' for nil:NilClass
from (irb):6:in `bar'
from (irb):8

I also consider yield() to be more idiomatic unless you need to
manipulate or pass along the proc object to another method.
 
T

Trans

Hi --





I think that's premature pessimization :) See my other post for some
benchmarks on yield vs. capture-and-call.

Is there no way for Ruby to optimize in the &block case? After all,
they are functionally equivalent if you only use #call on the block.

T.
 
P

Pit Capitain

2007/8/6 said:
When writing API-style code, I always start with &block because it's only a
matter of time before I refactor the code and start passing that &block into
a helper method.

Phlip, you can do the equivalent with yield:

def x(&b); xx(&b); end
def xx(&b); b.call; end

vs.

def y; yy { yield }; end
def yy; yield; end

Also in this case the yield version is much faster.

Regards,
Pit
 
P

Phlip

Pit said:
Phlip, you can do the equivalent with yield:

def x(&b); xx(&b); end
def xx(&b); b.call; end

vs.

def y; yy { yield }; end
def yy; yield; end

Also in this case the yield version is much faster.

What if the block were optional?

(I forgot to mention that's typical of API-style code...)
 
P

Pit Capitain

2007/8/6 said:
You can use block_given? to find out if a block was provided.

Yes, but not in the helper method. In the code I've shown there's
always a block passed to the helper method. Phlip has a point here.

Regards,
Pit
 
D

dblack

Hi --

Yes, but not in the helper method. In the code I've shown there's
always a block passed to the helper method. Phlip has a point here.

I still wouldn't use block.call automatically. I'd stick to yield
until it becomes necessary to wrap it in the slower construct.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
P

Phlip

Yes, but not in the helper method. In the code I've shown there's
That's very easy to solve:

What about the two kinds of performance? Programmer performance and
program performance?

This permits only one (DRY) call to y:
y { yield if block_given? }

So is it performant? Note someone here asserted that yield was faster,
so are these constructions still faster than a brute-force 'block.call
if block' ?

When I see 'if block' I think of comparing the RVALUE type field to
nil or false. Just a bit mask.
 
D

dblack

Hi --

What about the two kinds of performance? Programmer performance and
program performance?

This permits only one (DRY) call to y:


So is it performant? Note someone here asserted that yield was faster,
so are these constructions still faster than a brute-force 'block.call
if block' ?

When I see 'if block' I think of comparing the RVALUE type field to
nil or false. Just a bit mask.

Here are some benchmarks, for the two conditionals with and without a
block:

david-a-blacks-computer:~/hacking dblack$ cat yi.rb
require 'benchmark'
include Benchmark

def x(&b)
b.call if b
end

def y
yield if block_given?
end


n = 1000000

bmbm do |b|
b.report("b") { n.times { x { } } }
b.report("y") { n.times { y { } } }
b.report("b-no block") { n.times { x } }
b.report("y-no block") { n.times { y } }
end
david-a-blacks-computer:~/hacking dblack$ ruby yi.rb
Rehearsal ----------------------------------------------
b 3.780000 0.010000 3.790000 ( 3.798711)
y 0.700000 0.000000 0.700000 ( 0.695756)
b-no block 0.350000 0.000000 0.350000 ( 0.351118)
y-no block 0.380000 0.000000 0.380000 ( 0.390713)
------------------------------------- total: 5.220000sec

user system total real
b 3.810000 0.000000 3.810000 ( 3.846940)
y 0.700000 0.010000 0.710000 ( 0.760668)
b-no block 0.350000 0.000000 0.350000 ( 0.353762)
y-no block 0.390000 0.000000 0.390000 ( 0.390436)

So if the test is negative, there's not a huge difference; the
difference mainly comes when there is a block.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
R

Robert Dober

I am learning to consider 'yield' as a fossil of an early Ruby that could
not treat the bound block as a variable. I only use 'yield' as a slight
convenience - a few less characters to type - in application-specific code.
Do not do this, yield suffers a lot from a bad reputation it does not
deserve, see also David's benchmarks above.
When writing API-style code, I always start with &block because it's only a
matter of time before I refactor the code and start passing that &block into
a helper method.
Why? I do not think that this is necessary.
Just replace the refactoring
old: yield ==> something &blk + changing the def
with
new: yield ==> something &Proc.new

However I *never* use yield, but for a completely different reason, I
want my def to show if I yield or not.
In case I am unhappy with performance I can still change back.

Cheers
Robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,061
Latest member
KetonaraKeto

Latest Threads

Top