yield does not take a block

D

Daniel Brockman

Under ruby 1.9.0 (2005-06-23) [i386-linux], irb 0.9.5(05/04/13),
these forms are not permitted:

yield { .. }
yield &..

But these are okay:

Proc.new.call { .. }
Proc.new.call &..

This is an oversight, right?
 
N

nobuyoshi nakada

Hi,

At Tue, 28 Jun 2005 17:37:06 +0900,
Daniel Brockman wrote in [ruby-talk:146630]:
Under ruby 1.9.0 (2005-06-23) [i386-linux], irb 0.9.5(05/04/13),
these forms are not permitted:

yield { .. }
yield &..

But these are okay:

Proc.new.call { .. }
Proc.new.call &..

This is an oversight, right?

Right.


Index: eval.c
===================================================================
RCS file: /cvs/ruby/src/ruby/eval.c,v
retrieving revision 1.791
diff -U2 -p -r1.791 eval.c
--- eval.c 20 Jun 2005 09:59:58 -0000 1.791
+++ eval.c 28 Jun 2005 10:29:44 -0000
@@ -3201,4 +3201,18 @@ rb_eval(self, n)
result = Qundef; /* no arg */
}
+ if (node->nd_body) {
+ PUSH_BLOCK(0, 0);
+ if ((state = EXEC_TAG()) == 0) {
+ struct BLOCK *prev = ruby_block->prev;
+ *ruby_block = *prev;
+ ruby_block->prev = prev;
+ ruby_block->block_obj = rb_eval(self, node->nd_body);
+ SET_CURRENT_SOURCE();
+ result = rb_yield_0(result, 0, 0, YIELD_PROC_CALL, node->nd_state);
+ }
+ POP_BLOCK();
+ if (state) rb_jump_tag(state);
+ break;
+ }
SET_CURRENT_SOURCE();
result = rb_yield_0(result, 0, 0, 0, node->nd_state);
Index: parse.y
===================================================================
RCS file: /cvs/ruby/src/ruby/parse.y,v
retrieving revision 1.387
diff -U2 -p -r1.387 parse.y
--- parse.y 12 Jun 2005 16:56:05 -0000 1.387
+++ parse.y 28 Jun 2005 10:15:29 -0000
@@ -231,5 +231,5 @@ static void void_stmts_gen _((struct par
#define void_stmts(node) void_stmts_gen(parser, node)
static void reduce_nodes _((NODE**));
-static void block_dup_check _((NODE*));
+static NODE *add_iter_block _((NODE*,NODE*));

static NODE *block_append _((NODE*,NODE*));
@@ -1138,7 +1138,5 @@ command : operation command_args
$$ = new_fcall($1, $2);
if ($3) {
- block_dup_check($$);
- $3->nd_iter = $$;
- $$ = $3;
+ $$ = add_iter_block($$, $3);
}
fixpos($$, $2);
@@ -1162,7 +1160,5 @@ command : operation command_args
$$ = new_call($1, $3, $4);
if ($5) {
- block_dup_check($$);
- $5->nd_iter = $$;
- $$ = $5;
+ $$ = add_iter_block($$, $5);
}
fixpos($$, $1);
@@ -1186,7 +1182,5 @@ command : operation command_args
$$ = new_call($1, $3, $4);
if ($5) {
- block_dup_check($$);
- $5->nd_iter = $$;
- $$ = $5;
+ $$ = add_iter_block($$, $5);
}
fixpos($$, $1);
@@ -2561,28 +2555,4 @@ primary : literal
%*/
}
- | kYIELD '(' call_args rparen
- {
- /*%%%*/
- $$ = new_yield($3);
- /*%
- $$ = dispatch1(yield, dispatch1(paren, $3));
- %*/
- }
- | kYIELD '(' rparen
- {
- /*%%%*/
- $$ = NEW_YIELD(0, Qfalse);
- /*%
- $$ = dispatch1(yield, dispatch1(paren, arg_new()));
- %*/
- }
- | kYIELD
- {
- /*%%%*/
- $$ = NEW_YIELD(0, Qfalse);
- /*%
- $$ = dispatch0(yield0);
- %*/
- }
| kDEFINED opt_nl '(' {in_defined = 1;} expr rparen
{
@@ -2610,7 +2580,5 @@ primary : literal
{
/*%%%*/
- block_dup_check($$);
- $2->nd_iter = $1;
- $$ = $2;
+ $$ = add_iter_block($1, $2);
fixpos($$, $1);
/*%
@@ -3215,7 +3183,5 @@ block_call : command do_block
{
/*%%%*/
- block_dup_check($1);
- $2->nd_iter = $1;
- $$ = $2;
+ $$ = add_iter_block($1, $2);
fixpos($$, $1);
/*%
@@ -3296,4 +3262,20 @@ method_call : operation paren_args
%*/
}
+ | kYIELD paren_args
+ {
+ /*%%%*/
+ $$ = new_yield($2);
+ /*%
+ $$ = dispatch1(yield, dispatch1(paren, $2));
+ %*/
+ }
+ | kYIELD
+ {
+ /*%%%*/
+ $$ = NEW_YIELD(0, Qfalse);
+ /*%
+ $$ = dispatch0(yield0);
+ %*/
+ }
| tLPAREN compstmt ')' paren_args
{
@@ -7212,11 +7194,22 @@ aryset_gen(parser, recv, idx)
}

-static void
-block_dup_check(node)
- NODE *node;
+static NODE *
+add_iter_block(node, block)
+ NODE *node, *block;
{
- if (node && nd_type(node) == NODE_BLOCK_PASS) {
- compile_error(PARSER_ARG "both block arg and actual block given");
+ if (node) {
+ switch (nd_type(node)) {
+ case NODE_YIELD:
+ if (!node->nd_body) {
+ nd_set_type(block, NODE_LAMBDA);
+ node->nd_body = block;
+ return node;
+ }
+ case NODE_BLOCK_PASS:
+ compile_error(PARSER_ARG "both block arg and actual block given");
+ }
}
+ block->nd_iter = node;
+ return block;
}

@@ -7794,13 +7787,22 @@ new_yield(node)
{
long state = Qtrue;
+ NODE *body = 0;

if (node) {
- no_blockarg(node);
- if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) {
+ switch (nd_type(node)) {
+ case NODE_BLOCK_PASS:
+ body = node;
+ node = node->nd_iter;
+ nd_set_type(body, NODE_LAMBDA);
+ body->nd_iter = 0;
+ break;
+ case NODE_ARRAY:
+ if (node->nd_next != 0) break;
node = node->nd_head;
state = Qfalse;
- }
- else if (node && nd_type(node) == NODE_SPLAT) {
+ break;
+ case NODE_SPLAT:
state = Qtrue;
+ break;
}
}
@@ -7808,5 +7810,7 @@ new_yield(node)
state = Qfalse;
}
- return NEW_YIELD(node, state);
+ node = NEW_YIELD(node, state);
+ node->nd_body = body;
+ return node;
}
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: yield does not take a block"

|Under ruby 1.9.0 (2005-06-23) [i386-linux], irb 0.9.5(05/04/13),
|these forms are not permitted:
|
| yield { .. }
| yield &..
|
|But these are okay:
|
| Proc.new.call { .. }
| Proc.new.call &..
|
|This is an oversight, right?

yield is a keyword to pass a value (and control) to the block attached
to the method. I feel strange when I see yield passes a block to an
outer block.

matz.
 
J

James Britt

Yukihiro Matsumoto wrote:
...
yield is a keyword to pass a value (and control) to the block attached
to the method. I feel strange when I see yield passes a block to an
outer block.


Is that good strange or bad strange?

James
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: yield does not take a block"

|> yield is a keyword to pass a value (and control) to the block attached
|> to the method. I feel strange when I see yield passes a block to an
|> outer block.
|
|Is that good strange or bad strange?

I feel bad strange. Do you?

matz.
 
J

James Britt

Yukihiro said:
Hi,

In message "Re: yield does not take a block"

|> yield is a keyword to pass a value (and control) to the block attached
|> to the method. I feel strange when I see yield passes a block to an
|> outer block.
|
|Is that good strange or bad strange?

I feel bad strange. Do you?

No. Good strange.


James
 
D

Daniel Brockman

Yukihiro Matsumoto said:
yield is a keyword to pass a value (and control) to the block
attached to the method. I feel strange when I see yield passes a
block to an outer block.

I feel strange when I see these are not equivalent:

def foo1 &block
block.call &bar
end

def foo2
Proc.new.call &bar
end

def foo3 &block
yield &bar
end

The middle one may be a weird idiom that should be phased out, but I
really think that calling a block captured into a variable should be
equivalent to using yield.

If foo1 and foo3 are not equivalent, then we have an inconsistency.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: yield does not take a block"

|I feel strange when I see these are not equivalent:
|
| def foo1 &block
| block.call &bar
| end
|
| def foo2
| Proc.new.call &bar
| end
|
| def foo3 &block
| yield &bar
| end

|If foo1 and foo3 are not equivalent, then we have an inconsistency.

Indeed we have inconsistency here if those two are not equivalent, but
I don't think consistency matters most in this case. Using yield
emphasizes passing a value and control to a block given to the method.
On the other hand, "call"ing an block argument (&block) emphasizes
treating a block as a procedural object. If they are focusing
different aspect, they might not be exact same.

It's not a technical issue. Just a matter of how we feel when we see

yield &bar

or

yield {...}

I feel something strange (bad strange) when I see this, just because
it passes a block to another block, which is apparently strange,
whereas

block.call &bar

does not make me feel bad strange, since it is valid syntax in the
microscopic view, even when it is semantically strange.

If you have shown me the reason "yield &bar" is not strange (and is
useful) other than consistency, I'd be glad to apply the Nobu's patch
in [ruby-talk:146636].

matz.
 
E

Eric Mahurin

--- Yukihiro Matsumoto said:
Hi,

In message "Re: yield does not take a block"
on Wed, 29 Jun 2005 02:30:05 +0900, Daniel Brockman

|I feel strange when I see these are not equivalent:
|
| def foo1 &block
| block.call &bar
| end
|
| def foo2
| Proc.new.call &bar
| end
|
| def foo3 &block
| yield &bar
| end

|If foo1 and foo3 are not equivalent, then we have an
inconsistency.

Indeed we have inconsistency here if those two are not
equivalent, but
I don't think consistency matters most in this case. Using
yield
emphasizes passing a value and control to a block given to
the method.
On the other hand, "call"ing an block argument (&block)
emphasizes
treating a block as a procedural object. If they are
focusing
different aspect, they might not be exact same.

It's not a technical issue. Just a matter of how we feel
when we see

yield &bar

or

yield {...}

I feel something strange (bad strange) when I see this, just
because
it passes a block to another block, which is apparently
strange,
whereas

block.call &bar

does not make me feel bad strange, since it is valid syntax
in the
microscopic view, even when it is semantically strange.

If you have shown me the reason "yield &bar" is not strange
(and is
useful) other than consistency, I'd be glad to apply the
Nobu's patch
in [ruby-talk:146636].

matz.


I tend to agree that the yield should be made consistent with
block.call such that yield would basically be just an alias for
block.call (where block is the block given to the current
method). I think any keyword that can reasonably be made to
look like a method call, should. Makes the syntax more
consistent. If you were starting over, I'd even suggest things
like if/while to be like (or actually) method calls.

but...

Personally, I don't think having yield as a keyword really adds
that much value to the language. The only advantages I see
over explicitly having a &block arg and using block.call are a)
it looks like smalltalk, and b) rdoc parses it better. I think
showing the &block on the def line makes the method interface
clearer up front. I don't use yield at all and haven't missed
it.




__________________________________
Do you Yahoo!?
Yahoo! Mail - Find what you need with new enhanced search.
http://info.mail.yahoo.com/mail_250
 
B

Bertram Scharpf

Hi,

Am Mittwoch, 29. Jun 2005, 01:19:16 +0900 schrieb Yukihiro Matsumoto:
In message "Re: yield does not take a block"

|> yield is a keyword to pass a value (and control) to the block attached
|> to the method. I feel strange when I see yield passes a block to an
|> outer block.
|
|Is that good strange or bad strange?

I feel bad strange. Do you?

Logic demands to allow it. Prohibiting it would mean to
introduce an exception rule (POLS?).

Anyway, the programmer should be saved from fooling himself
inventing more and more complicated constructions.

Bad strange.

Bertram
 
J

James Britt

Bertram said:
Hi,

Am Mittwoch, 29. Jun 2005, 01:19:16 +0900 schrieb Yukihiro Matsumoto:



Logic demands to allow it. Prohibiting it would mean to
introduce an exception rule (POLS?).

There's a lot to be said for consistency in a language, to avoid the
complexity introduced by "This is the case, except when it's not."

Ruby has fairly few of these instances, which is part of its appeal for me.

It's nice to be able to layout a small set of ground rules and say that
everything you can derive from them is valid.
Anyway, the programmer should be saved from fooling himself
inventing more and more complicated constructions.

Saved in what way?

Languages should be designed to enable developers, not protect them from
themselves.


James
--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
 
D

Daniel Brockman

Yukihiro Matsumoto said:
If you have shown me the reason "yield &bar" is not strange (and is
useful) other than consistency, I'd be glad to apply the Nobu's
patch in [ruby-talk:146636].

I don't have any other reason than consistency. Having used Ruby 1.8
up until yesterday, yield and Proc.new.call are equivalent in my mind.
It seems I will have to drop that identification when moving to 1.9.

The fact that blocks cannot take blocks pre-1.9 has always bugged me,
and I certainly don't think there's anything strange about this code:

define_method :foo do |*args, &block| ... end

However, I agree that most uses of these metablocks (if you will)
probably do not coincide with the use cases of the yield keyword.

So I will not be upset if Nobu's patch is not applied. I will just
make a mental note that Yield Does Not Take a Block and move on.
Should I ever be tempted to pass a block to yield, I will remember
that it doesn't work, and simply call the block explicitly instead.

I guess the reason why yield &block is not strange to me is that the
yield statement yields values, and blocks are just special values.
 
R

Robert Klemme

Eric said:
--- Yukihiro Matsumoto said:
Hi,

In message "Re: yield does not take a block"
I feel strange when I see these are not equivalent:

def foo1 &block
block.call &bar
end

def foo2
Proc.new.call &bar
end

def foo3 &block
yield &bar
end
If foo1 and foo3 are not equivalent, then we have an inconsistency.

Indeed we have inconsistency here if those two are not
equivalent, but
I don't think consistency matters most in this case. Using
yield
emphasizes passing a value and control to a block given to
the method.
On the other hand, "call"ing an block argument (&block)
emphasizes
treating a block as a procedural object. If they are
focusing
different aspect, they might not be exact same.

It's not a technical issue. Just a matter of how we feel
when we see

yield &bar

or

yield {...}

I feel something strange (bad strange) when I see this, just
because
it passes a block to another block, which is apparently
strange,
whereas

block.call &bar

does not make me feel bad strange, since it is valid syntax
in the
microscopic view, even when it is semantically strange.

If you have shown me the reason "yield &bar" is not strange
(and is
useful) other than consistency, I'd be glad to apply the
Nobu's patch
in [ruby-talk:146636].

matz.


I tend to agree that the yield should be made consistent with
block.call such that yield would basically be just an alias for
block.call (where block is the block given to the current
method). I think any keyword that can reasonably be made to
look like a method call, should. Makes the syntax more
consistent. If you were starting over, I'd even suggest things
like if/while to be like (or actually) method calls.

but...

Personally, I don't think having yield as a keyword really adds
that much value to the language. The only advantages I see
over explicitly having a &block arg and using block.call are a)
it looks like smalltalk, and b) rdoc parses it better. I think
showing the &block on the def line makes the method interface
clearer up front. I don't use yield at all and haven't missed
it.

Note though that yield and &c.call work quite differently. This has for
example performance implications.

Kind regards

robert
 
E

Eric Mahurin

--- Robert Klemme said:
Note though that yield and &c.call work quite differently.=20
This has for example performance implications.

I didn't know about this advantage. From my quick benchmarks,
I found yield to take half the time as Proc#call in a loop with
the block being empty. I'd imagine the performance difference
is because yield is working with a statically typed Proc,
whereas call is using a normal dynamically typed object to
#call. I'd imagine as ruby matures, the performance difference
between these two would become negligible or zero. I'd hope at
some point ruby would make optimizations when it knows the
exact class of an object - do method lookup at compile time.



=09
__________________________________=20
Yahoo! Mail Mobile=20
Take Yahoo! Mail with you! Check email on your mobile phone.=20
http://mobile.yahoo.com/learn/mail=20
 
R

Robert Klemme

Eric said:
I didn't know about this advantage. From my quick benchmarks,
I found yield to take half the time as Proc#call in a loop with
the block being empty. I'd imagine the performance difference
is because yield is working with a statically typed Proc,
whereas call is using a normal dynamically typed object to
#call.

Yes. More preceisely the overhead of &b.call is in the creation of the
proc (i.e. transforming the block, storing the closure and registering the
result with GC) - with 'yield' there is no object around.
I'd imagine as ruby matures, the performance difference
between these two would become negligible or zero.

Probably but IMHO not very likely. Maybe the gap closes but my guess
would be that it remains significant.
I'd hope at
some point ruby would make optimizations when it knows the
exact class of an object - do method lookup at compile time.

IMHO this is close to impossible as knowing the class of an object tells
you nothing definite about which methods it supports and what signature
they have. (see all the discussions about static typing)

Kind regards

robert
 
E

Eric Mahurin

--- Robert Klemme said:
time.
=20
IMHO this is close to impossible as knowing the class of an
object tells
you nothing definite about which methods it supports and what
signature
they have. (see all the discussions about static typing)

Probably correct. I think there may be a little too much
flexibility in the ruby language that will prevent future
optimizations. I think the destructive modification of a class
and of an object's class (making its class a singleton) are the
main culprits. If these were non-destructive (returning a new
class or new object with a singleton class), it would have
allowed more optimization. To me, discussions about
destructive vs. non-destructive methods (reverse! vs. reverse)
are nothing compared to these.



=09
____________________________________________________=20
Yahoo! Sports=20
Rekindle the Rivalries. Sign up for Fantasy Football=20
http://football.fantasysports.yahoo.com
 
A

Adam P. Jenkins

Eric said:
Personally, I don't think having yield as a keyword really adds
that much value to the language. The only advantages I see
over explicitly having a &block arg and using block.call are a)
it looks like smalltalk, and b) rdoc parses it better. I think
showing the &block on the def line makes the method interface
clearer up front. I don't use yield at all and haven't missed
it.

I completely agree with this. I think closures are one of the most
useful features of Ruby, but I still don't understand what the point of
having blocks and yield is. In all other languages I've used that have
closures, such as Scheme, ML, Haskell, OCaml, and even Javascript, a
closure is just an object like any other object, that happens to have a
special literal syntax for creating them. If you want to pass a closure
to another function, you just pass it like any other argument.

The way ruby does it with blocks/yield has several disadvantages:

1) There's an arbitrary limit of 1 on the number of blocks you can pass
to a function. If you want to pass more than 1, you need to convert all
but the last block to a proc.

2) The block argument can be implicit, so you can't see from the
function signature that there is a block argument.

3) You get a problem akin to Java's primitive/Object separation, where
in some contexts you need a block, and have to convert another callable
object to a block, and in other contexts you need a proc, and have to
convert a block to a proc.

Let me expand on the last item. In functional languages like Scheme,
ML, or Haskell, all functions are closures, whether they're named or
anonymous, and can be used interchangeably. So, say I wanted to apply a
function to each element of a list, I'd write something like this in ML:

for_each myArray print

for_each takes a list as its first argument, and a function with 1
parameter as its second argument, and applies the function to each
element of the list. In Ruby, since there's a distinction between
blocks, closures, and functions, I can't just pass print, even though
it's a function of 1 parameter. Rather I have to wrap print in a block:

myArray.each { |elem| print elem }

Wouldn't it have been cool to just be able to write:

myArray.each print

Overall, it's not a big problem, but I do think the fact that there's a
distinction between blocks, procs, and functions is one of the main
warts in the Ruby language.

Adam
 
D

Daniel Brockman

Eric Mahurin said:
I think the destructive modification of a class and of an object's
class (making its class a singleton) are the main culprits.

However, I ``destructively'' modify standard library classes and
modules all the time. This is a major selling point for Ruby.
If these were non-destructive (returning a new class or new object
with a singleton class), it would have allowed more optimization.

I don't see how returning a new class could be useful in any way.
For example, what should happen if you say this?

class Numeric ; def infinite? ; false end end
 
K

Kent Sibilev

Yes, but consider this:

irb(main):001:0> def display x
irb(main):002:1> puts x
irb(main):003:1> end
=3D> nil
irb(main):004:0> (1..5).each &method:)display)
1
2
3
4
5
=3D> 1..5
irb(main):005:0>

Kent.
 
E

Eric Mahurin

--- "Adam P. Jenkins said:
Rather I have to wrap print
in a block:
=20
myArray.each { |elem| print elem }
=20
Wouldn't it have been cool to just be able to write:
=20
myArray.each print

The reason you can't is because a method name by itself calls
the method instead of returning the method object. If you had
to put () to make the call and the method name by itself
returned the method object (I would have preferred that), you
could do this:

myArray.each(&print)

but, this should work fine right now:

myArray.each(&method:)print))



=09
__________________________________=20
Discover Yahoo!=20
Have fun online with music videos, cool games, IM and more. Check it out!=
=20
http://discover.yahoo.com/online.html
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top