Using yield

I forward blocks a lot in my own code, and wish there was a way to get
around using the &block syntax. I suspect that someone else has
probably suggested this already, but what about a &yield keyword? ie.:

  def thrice
    3.times &yield
  end

Assuming it would be optimized, and wouldn't create a Proc object,
this could be a nicer way of forwarding a block.

cheers,
Mark

···

On Wed, 8 Dec 2004 04:19:11 +0900, David A. Black <dblack@wobblini.net> wrote:

As for forwarding a block to a new method -- isn't &b the only way?
Or am I behind the times on automatic propagation?

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0412071110500.21344-100000@wobblini...

Hi --

> 13:21:36 [robert.klemme]: ruby yield-vs-block.rb
> user system total real
> yield 0.250000 0.000000 0.250000 ( 0.249000)
> block 0.125000 0.000000 0.125000 ( 0.124000)
> 13:21:44 [robert.klemme]: cat yield-vs-block.rb
>
> require 'benchmark'
> include Benchmark
>
> REP = 100000
>
> def bl_yield(n)
> n.times {|i| yield i}
> end
>
> def bl_fwd(n,&b)
> n.times(&b)
> end

Those aren't equivalent, though. The second one would have to be:

  n.times {|i| b.call(i) }

to compare the two idioms directly.

Hm, I'm not sure I agree here. My point was to compare performance of two
idioms achieving that a block handed to a method is called from another
method called within that method. You are right with respect to the
invocation chain though.

When I make that change I get:

yield 0.320000 0.000000 0.320000 ( 0.332194)
block 0.460000 0.000000 0.460000 ( 0.490299)

> I blieve the yield forwarding is slower because there is one more

call.

>
> > Wouldn't the &block form be more clutter? Well, not that it's so
> > much clutter in either case, but I think it would be slightly

wordier

> > (def five_time(&block) etc.)
>
> For forwarding I prefer &b because the original block is simply passed

on

> vs. a new block yields to the original block. That seems awkward to

me

> and seems to be slower, too. Personally I find the yield forwarding

more

> clutter. YMMV

The clutter point was (at least in my mind :slight_smile: strictly about the
difference between:

   def x
     yield
   end

and

   def x(&b)
     b.call
   end

As for forwarding a block to a new method -- isn't &b the only way?

Strictly speaking yes. But the idiom presented in my bl_yield() achieves
about the same. At least there are no semantic differences I'm aware of.

Or am I behind the times on automatic propagation? (But even in that
case I don't think supplying block #1 to method #2 is the same as
yielding to block #1 during method #1.)

What exactly do you think is the difference here? (Apart from performance
of course)

Kind regards

    robert

···

On Tue, 7 Dec 2004, Robert Klemme wrote:

Hi --

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0412071110500.21344-100000@wobblini...

Hi --

13:21:36 [robert.klemme]: ruby yield-vs-block.rb
                          user system total real
yield 0.250000 0.000000 0.250000 ( 0.249000)
block 0.125000 0.000000 0.125000 ( 0.124000)
13:21:44 [robert.klemme]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

def bl_yield(n)
  n.times {|i| yield i}
end

def bl_fwd(n,&b)
  n.times(&b)
end

Those aren't equivalent, though. The second one would have to be:

  n.times {|i| b.call(i) }

to compare the two idioms directly.

Hm, I'm not sure I agree here. My point was to compare performance of two
idioms achieving that a block handed to a method is called from another
method called within that method. You are right with respect to the
invocation chain though.

I think we're talking about different sets of "two idioms", but I see
what you mean about your set. As for....

Or am I behind the times on automatic propagation? (But even in that
case I don't think supplying block #1 to method #2 is the same as
yielding to block #1 during method #1.)

What exactly do you think is the difference here? (Apart from performance
of course)

... there's something nagging in my mind that I can't quite nail down,
having something to do with argument parsing, I think. It's probably
illusory. If I manage to figure out what it is, I'll post it :slight_smile:

David

···

On Wed, 8 Dec 2004, Robert Klemme wrote:

On Tue, 7 Dec 2004, Robert Klemme wrote:

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.61.0412080544030.25964@wobblini...

Hi --

>
> "David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
> news:Pine.LNX.4.44.0412071110500.21344-100000@wobblini...
>> Hi --
>>
>>
>>> 13:21:36 [robert.klemme]: ruby yield-vs-block.rb
>>> user system total real
>>> yield 0.250000 0.000000 0.250000 ( 0.249000)
>>> block 0.125000 0.000000 0.125000 ( 0.124000)
>>> 13:21:44 [robert.klemme]: cat yield-vs-block.rb
>>>
>>> require 'benchmark'
>>> include Benchmark
>>>
>>> REP = 100000
>>>
>>> def bl_yield(n)
>>> n.times {|i| yield i}
>>> end
>>>
>>> def bl_fwd(n,&b)
>>> n.times(&b)
>>> end
>>
>> Those aren't equivalent, though. The second one would have to be:
>>
>> n.times {|i| b.call(i) }
>>
>> to compare the two idioms directly.
>
> Hm, I'm not sure I agree here. My point was to compare performance of

two

> idioms achieving that a block handed to a method is called from

another

> method called within that method. You are right with respect to the
> invocation chain though.

I think we're talking about different sets of "two idioms",

Yes, we are / were. :slight_smile:

but I see
what you mean about your set. As for....

>> Or am I behind the times on automatic propagation? (But even in that
>> case I don't think supplying block #1 to method #2 is the same as
>> yielding to block #1 during method #1.)
>
> What exactly do you think is the difference here? (Apart from

performance

> of course)

.. there's something nagging in my mind that I can't quite nail down,
having something to do with argument parsing, I think. It's probably
illusory. If I manage to figure out what it is, I'll post it :slight_smile:

Thanks! I'm curios to learn what you find out. Wait...

If the other method (Fixnum#times in my example) would do different
yielding. Hm... I think I should redefine my set:

14:52:25 [09_Public]: ruby yield-vs-block.rb
                          user system total real
yield 0.265000 0.000000 0.265000 ( 0.263000)
yield_star 0.766000 0.000000 0.766000 ( 0.778000)
block 0.109000 0.000000 0.109000 ( 0.124000)
14:52:58 [09_Public]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

def bl_yield(n)
  n.times {|i| yield i}
end

def bl_yield_star(n)
  n.times {|*i| yield *i}
end

def bl_fwd(n,&b)
  n.times(&b)
end

bm(20) do |b|
  b.report("yield") do
    bl_yield(REP) {|i| i + 1}
  end

  b.report("yield_star") do
    bl_yield_star(REP) {|i| i + 1}
  end

  b.report("block") do
    bl_fwd(REP) {|i| i + 1}
  end
end

Uh, even worse performance for "yield *i". *shudder* But not really
unexpected. One more reason to use &b for forwarding.

Kind regards

    robert

···

On Wed, 8 Dec 2004, Robert Klemme wrote:
>> On Tue, 7 Dec 2004, Robert Klemme wrote:

I think my vague concern about argument parsing has to do with the
question of who gets to decide. There might be a case where for some
reason you wanted to do:

   def some_method(n)
     n.times {|*i| yield i}
   end

or something weird like that... but I guess that takes us away from
exact equivalence with &b, since now I'm not just passing the args to
yield one at a time as they come.

David

···

On Wed, 8 Dec 2004, Robert Klemme wrote:

If the other method (Fixnum#times in my example) would do different
yielding. Hm... I think I should redefine my set:

14:52:25 [09_Public]: ruby yield-vs-block.rb
                         user system total real
yield 0.265000 0.000000 0.265000 ( 0.263000)
yield_star 0.766000 0.000000 0.766000 ( 0.778000)
block 0.109000 0.000000 0.109000 ( 0.124000)
14:52:58 [09_Public]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

def bl_yield(n)
n.times {|i| yield i}
end

def bl_yield_star(n)
n.times {|*i| yield *i}
end

def bl_fwd(n,&b)
n.times(&b)
end

bm(20) do |b|
b.report("yield") do
   bl_yield(REP) {|i| i + 1}
end

b.report("yield_star") do
   bl_yield_star(REP) {|i| i + 1}
end

b.report("block") do
   bl_fwd(REP) {|i| i + 1}
end
end

Uh, even worse performance for "yield *i". *shudder* But not really
unexpected. One more reason to use &b for forwarding.

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.61.0412080603510.9910@wobblini...

> If the other method (Fixnum#times in my example) would do different
> yielding. Hm... I think I should redefine my set:
>
> 14:52:25 [09_Public]: ruby yield-vs-block.rb
> user system total real
> yield 0.265000 0.000000 0.265000 ( 0.263000)
> yield_star 0.766000 0.000000 0.766000 ( 0.778000)
> block 0.109000 0.000000 0.109000 ( 0.124000)
> 14:52:58 [09_Public]: cat yield-vs-block.rb
>
> require 'benchmark'
> include Benchmark
>
> REP = 100000
>
> def bl_yield(n)
> n.times {|i| yield i}
> end
>
> def bl_yield_star(n)
> n.times {|*i| yield *i}
> end
>
> def bl_fwd(n,&b)
> n.times(&b)
> end
>
> bm(20) do |b|
> b.report("yield") do
> bl_yield(REP) {|i| i + 1}
> end
>
> b.report("yield_star") do
> bl_yield_star(REP) {|i| i + 1}
> end
>
> b.report("block") do
> bl_fwd(REP) {|i| i + 1}
> end
> end
>
> Uh, even worse performance for "yield *i". *shudder* But not really
> unexpected. One more reason to use &b for forwarding.

I think my vague concern about argument parsing has to do with the
question of who gets to decide. There might be a case where for some
reason you wanted to do:

   def some_method(n)
     n.times {|*i| yield i}
   end

or something weird like that... but I guess that takes us away from
exact equivalence with &b, since now I'm not just passing the args to
yield one at a time as they come.

Yeah, the &equivalent would be

   def some_method(n,&b)
     n.times {|*i| b.call(i)}
   end

which suffers a similar detour as the yield approach. I think the most
common case for "block forwarding" does not involve argument mangling so
this is probably a special case.

Kind regards

    robert

···

On Wed, 8 Dec 2004, Robert Klemme wrote: