Default argument values for blocks

C

Caleb Clausen

Ok, this has been bugging me ever since the last time this issue came
up on redhanded.

I think that what we'd all like to see, ideally, is a default argument
syntax that looks like default arguments in def headers do. That is:

proc {|a=3Dexpr| ... }

This creates an ambiguity if expr contains an |-operator. Which should
be resolved by requiring parentheses around the expression in that
case. This seemed like it ought to be pretty easy to implement.... so
I tried to see if I could get my RubyLexer to support the above
syntax. And, it took only about 15 minutes to code and test a tweak to
RubyLexer to make it all work right. (Actually, there's a small
problem with my tweak as currently written; it ought to be possible to
escape | with a dot as well as parentheses.... but that'd be even
easier to fix.)

(It may seem strange that I decided to address this in a _lexer_ since
it's really a _parsing_ problem. However, RubyLexer also has to be
half-a-parser, since it's a stand-alone lexer. Normally, lexers get
hints from their parsers about what the context is for the current
token; I wanted (perhaps foolishly, as it created a big pile of extra
work) to have RubyLexer work correctly in all cases even if there is
no parser. So, it is actually appropriate to hack RubyLexer to handle
this case.)

Flushed from my success with RubyLexer, I decided to see if I could
repeat it in ruby 1.9's yacc grammar. After some false starts, I
figured out that resolving the ambiguity meant throwing in a %prec
modifier for the right rule... and I found the rule in need of
hackery: any rule with |-as-goalpost should be very high precedence in
order to avoid the interpretation of |-as-operator.

But no matter how many different variations I tried, I could not get
ruby to build correctly again after tossing in a %prec (and modifying
the block argument list to accept default parameters). I don't
remember the details now... it might have been dieing merely because I
had made no effort to ensure that whatever comes after the parser is
able to understand the new syntax tree format that this change
necessitates. However, my now vague memory of this is that the error
seemed to be in the parser itself.

A little reflection should have shown me the futility.... Presumably
Matz and Nobu have tried all this already and been frustrated by the
same obstacle that I had been beating my brain bloody against. Why
should I, with my pathetic and measly yacc skills, expect to do any
better?

But... does anyone know why I couldn't get yacc to do what was so easy
in my hand-coded lexer? I've long since erased my yacc hacks in
disgust, but if anyone's interested enough to want to see them, they
can easily be reproduced.
 
C

Clifford Heath

Caleb said:
But... does anyone know why I couldn't get yacc to do what was so easy
in my hand-coded lexer? I've long since erased my yacc hacks in
disgust, but if anyone's interested enough to want to see them, they
can easily be reproduced.

I forget the details now, but C has the same problem with the comma
operator, when it occurs in a function-call parameter list - the
precedence changes. You might see how that is handled in one of the
many yacc grammars for C.

FWIW, I think that the goalpost symbol was a bad choice by matz, but
regardless of that, you're aiming at the right work-around.

Clifford Heath.
 
T

Trans

Christophe said:
collection.each:(foo="bar"){puts foo}

Ha! How ironic, I thought of that last night too. But after playing
with I decided I did not like it. The colon has too much "seperation"
semantic, I think. And think of the Python converts ;) Indeed I have. I
was thinking that the colon might be a nice way to go without 'do':

collection.each:
puts "foo"
end

With todays kind of block arguments:

collection.each: |foo|
puts foo
end

But we're after outer arguments, so with Matz/Perl notation:

collection.each->(foo):
puts foo
end

I've tried every punctuation combination I can think of. I think I like
that best. Though I might suggest a word form for those who do not:

collection.each fn(foo):
puts foo
end

T.
 
T

Thomas

Mostly because yacc does not allow it. It confuses
lambda { |foo = bar| puts foo }

as

lambda { |foo = (bar| puts foo) }

and causes syntax error.

(Mostly) out of curiosity:

Would this

whatever_method {(x, foo=bar) puts x,foo}

and also this

whatever_method do(x, foo=bar)
puts x,foo
end

be possible.

Cheers,
Thomas.
 
P

Phil Tomson

Christophe said:
En réponse à David A. Black :

No, but do you really want to have "->" instead?

I think the other option is to pass on the whole idea of default
arguments to blocks for now if we can't get the nice lambda
{|foo='bar'| puts foo } sort of syntax to work. The idea could be
resurrected later when we have a better parser technology. Why open
the door for this arrow operator for this very special (and likely very
rarely used) case.
The problem is that default values for blocks *are* now in Ruby 1.9, and
thus are probably going to be in Ruby2,

1.9 is a testing ground for ideas for Ruby2 (at least that's how I
understand it). I don't think that everything in 1.9 (as it currently
stands) will be exactly as it is in 2.0.
and use right now the ugly "->".
If they are going to be there, let's have at least a non-intrusive
syntax. Yes it's arbitrary, but just as much as using : for symbols and
@ for instance variables. At the end of the day, every syntax in a
language is a matter of choice and convo ention, i.e. it's arbitrary.

Sure that's true, however, it doesn't seem like a good idea to
introduce a whole new syntax to support a feature that seems to have
limited utility. Especially when the new syntax essentially introduces
a new and very different way to define blocks (a central feature of
Ruby).
I also don't really see why blocks arguments should have default values.
But if we're going to have them, at least let's have an OK syntax for
them, instead of an ugly beast.

As I said above, I think that if we can't have the syntax that most
everyone agrees is the most pleasant and the one which causes the least
disruption ( {|foo='bar'| } ) in that it doesn't introduce a special
case then perhaps we should forgo the support for the feature until we
can get the syntax that fits best with the way things are done now.
I'd just hate to see this '->' thingy introduced and in all the Ruby
books and then someone comes along and figures out how to parse the
pathological case: {|foo=a|b| } (something very hard and perhaps
impossible in yacc, but who knows what some yacc wizard might be able
to come up with?). Then what? do we deprecate '->'? Probably not,
because there will be code out in the wild that depends on it. At that
point we have to live with it.

Phil
 
C

Christophe Grandsire

Selon Phil Tomson said:
I think the other option is to pass on the whole idea of default
arguments to blocks for now if we can't get the nice lambda
{|foo=3D'bar'| puts foo } sort of syntax to work. The idea could be
resurrected later when we have a better parser technology. Why open
the door for this arrow operator for this very special (and likely very
rarely used) case.

True. I'd rather have it like that too. But it seems a sizeable amount of=
people
*want* default block argument values and aren't willing to wait until Rub=
y3. So
if we *have* to have this new syntax, at least let's not use the arrow an=
d let
us find a better syntax. If at the end the whole idea is thrown away and =
Ruby2
doesn't support it, it won't be a problem anyway. I just want to secure t=
he
ground in the case the syntax finds its way into Ruby2. Better safe than =
sorry.
1.9 is a testing ground for ideas for Ruby2 (at least that's how I
understand it). I don't think that everything in 1.9 (as it currently
stands) will be exactly as it is in 2.0.

No, not their look and feel in any case. But for the case it would stay, =
let us
work out a "least ugly" syntax.
Sure that's true, however, it doesn't seem like a good idea to
introduce a whole new syntax to support a feature that seems to have
limited utility. Especially when the new syntax essentially introduces
a new and very different way to define blocks (a central feature of
Ruby).

I completely agree. But once again you argue at cross-purpose from me. I'=
d
rather do without default block argument values until we find a parser th=
at
does "{ |foo=3Dbar| ... }" correctly. But since the feature seems to be s=
een of
enough importance to have found its way in Ruby 1.9 already, in a way tha=
t I
find too ugly for words, I find it more productive to propose an alternat=
ive
rather than just say that I don't want the feature at all. Once again, I'=
d
prefer not have the new syntax at all, but if we are to have it at least =
have
something that isn't too ugly.
As I said above, I think that if we can't have the syntax that most
everyone agrees is the most pleasant and the one which causes the least
disruption ( {|foo=3D'bar'| } ) in that it doesn't introduce a special
case then perhaps we should forgo the support for the feature until we
can get the syntax that fits best with the way things are done now.

It's OK by me. But I was under the impression that this had been discusse=
d
already and that it was decided to use default block argument values anyw=
ay.
I'd just hate to see this '->' thingy introduced and in all the Ruby
books and then someone comes along and figures out how to parse the
pathological case: {|foo=3Da|b| } (something very hard and perhaps
impossible in yacc, but who knows what some yacc wizard might be able
to come up with?). Then what? do we deprecate '->'? Probably not,
because there will be code out in the wild that depends on it. At that
point we have to live with it.

Exactly! Hence my proposal for ":". At least we do *not* introduce a comp=
letely
new symbol (Eric's proposal of "." is also good for this purpose). As for
deprecating features, it can be done. I doubt after Ruby2 is out that the
language will not evolve anymore to a point where compatibility-breaking
changes won't have to be added (like they are at the moment for Ruby2).
Deprecating features can be done then.

We agree on many things. The only thing we disagree on is whether it is u=
seful
to discuss a new syntax we actually don't want to appear in the language.=
You
say it's not, and we should just argue against the whole feature if we ca=
n't get
it without a whole new syntax. I say it is, because the fact that there i=
s a
discussion means there *is* a chance that the new syntax will find its wa=
y into
Ruby2, in which case I'd rather have it in a way I can live with rather t=
han in
a way that I cannot.
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.
 
C

Christophe Grandsire

Selon Thomas said:
whatever_method do(x, foo=3Dbar) {puts x,foo}

Despite the fact that it looks like a monstruous cross between do... end =
and
{...}, and would allow the stuttering:

whatever_method do(x, foo=3Dbar) do puts x,foo end

I don't find it that bad at all. If we go for a keyword rather than symbo=
l
solution, I wouldn't mind it. For returning a Proc, using lambda is still=
the
best idea so far:

a =3D lambda(x, foo=3Dbar) {puts x,foo}

Maybe "with"?

whatever_method with(x, foo=3Dbar) {puts x,foo}
whatever_method with(x, foo=3Dbar) do puts x,foo end

I still like my colon better ;) .
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.
 
T

Thomas Sondergaard

Yukihiro said:
Hi,

In message "Re: Default argument values for blocks"

|I definitely think a keyword would be better than -> .

Maybe. Some prefer verbosity to clarity. So what keyword you think
"the best"?

matz.

Instead of introducing a new keyword for lambdas wouldn't it be simpler
to introduce a new assignment operator for default arguments, e.g :=
 
N

nobuyoshi nakada

Hi,

At Thu, 13 Oct 2005 14:33:08 +0900,
Caleb Clausen wrote in [ruby-talk:160349]:
A little reflection should have shown me the futility.... Presumably
Matz and Nobu have tried all this already and been frustrated by the
same obstacle that I had been beating my brain bloody against. Why
should I, with my pathetic and measly yacc skills, expect to do any
better?

Me? I'd tried it once by differentiating goal-post from
bit-or in the lexer.

http://www.rubyist.net/~nobu/t/20050805.html#p01

I don't remember the detail too, however the essential idea
seems to be adopted partly.
 
E

Eric Mahurin

--- nobuyoshi nakada said:
Hi,
=20
At Thu, 13 Oct 2005 14:33:08 +0900,
Caleb Clausen wrote in [ruby-talk:160349]:
A little reflection should have shown me the futility.... Presumably
Matz and Nobu have tried all this already and been frustrated by the
same obstacle that I had been beating my brain bloody against. Why
should I, with my pathetic and measly yacc skills, expect to do any
better?
=20
Me? I'd tried it once by differentiating goal-post from
bit-or in the lexer.
=20
http://www.rubyist.net/~nobu/t/20050805.html#p01
=20
I don't remember the detail too, however the essential idea
seems to be adopted partly.

Anybody ever just consider making a new expression (or maybe
RHS?) rule just for these default values such that "|" was not
in this expression? (...) in this or-less expression would
still use the normal expression. Wouldn't this solve the
problem? You'd just be forced to use (...) whenever you wanted
to "|". That doesn't seem so bad.

... { |a,b=3D1|2|a*b } # b default: 1, code: 2|a*b
... { |a,b=3D(1|2)|a*b } # b default: (1|2), code: a*b
... { |a,b=3D1+2|a*b } # b default: 1+2, code: a*b

I don't see any ambiguity doing it this way.

Of course the other reason for the new syntax is a shortented
lambda syntax. I'd say just to do what was being done
previously in 1.9: use {} and resolve ambiguities with {} hash.
The simple solution to resolve ambiguities is in this lambda,
require ||. If you didn't want the ||, you would have to use
the equivalent: {|*| ... }. When the parser encounters a "{",
you only have to lookahead one token to see whether it is a
lambda (next token: "|") or a hash (next token: not a "|").

Another option for a shortened lambda is to put some symbol
(that doesn't make sense in front of a hash) in front of a
block to make it a lambda. A few options for that would be: &{
}, \{ }, .{ }, ^{ }. You may even be able to treat some of
these as simply another Kernel method (alias for lambda).




__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around=20
http://mail.yahoo.com=20
 
D

David A. Black

Hi --

Of course the other reason for the new syntax is a shortented
lambda syntax. I'd say just to do what was being done
previously in 1.9: use {} and resolve ambiguities with {} hash.
The simple solution to resolve ambiguities is in this lambda,
require ||. If you didn't want the ||, you would have to use
the equivalent: {|*| ... }. When the parser encounters a "{",
you only have to lookahead one token to see whether it is a
lambda (next token: "|") or a hash (next token: not a "|").

No, please not that. I want to know, when I look at:

obj.meth {

exactly what it is, as I can now. I don't want to have to read ahead
to the secret punctuation signal.

There must be some way out of this that doesn't involve adding all
sorts of significant punctuation to the language. Or is there? I'm
starting to wonder whether maybe Ruby has hit the point where, short
of \&*{[ and friends, there are no ways left to represent all the
states and options people want. I literally can't remember the last
time an enhancement (other than just a method addition) was suggested
that didn't involve more punctuation.

I think Ruby's cleanness of line is a *major* reason that people are
attracted to Ruby, and it won't take more than a few new punctuation
workarounds to make a serious dent in that look.


David
 
C

Christophe Grandsire

Selon Eric Mahurin said:
Anybody ever just consider making a new expression (or maybe
RHS?) rule just for these default values such that "|" was not
in this expression? (...) in this or-less expression would
still use the normal expression. Wouldn't this solve the
problem? You'd just be forced to use (...) whenever you wanted
to "|". That doesn't seem so bad.

... { |a,b=3D1|2|a*b } # b default: 1, code: 2|a*b
... { |a,b=3D(1|2)|a*b } # b default: (1|2), code: a*b
... { |a,b=3D1+2|a*b } # b default: 1+2, code: a*b

I don't see any ambiguity doing it this way.

Yacc does. Even the simple { |a,b=3D1| a*b } fails because Yacc treats it=
as { |
a,b=3D(1| a*b) }. And that *cannot* be solved in the current state of Yac=
c. The
problem is that there doesn't seem to be *any* solution that solves both =
this
problem and the problem of allowing bitwise or's in default arguments, sh=
ort of
renaming the bitwise or, which would be such a compatibility break that i=
t isn't
worth contemplating.

This is at least my understanding of the problem.
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.
 
E

Eric Mahurin

--- Christophe Grandsire said:
=20
Yacc does. Even the simple { |a,b=3D1| a*b } fails because Yacc
treats it as { |
a,b=3D(1| a*b) }. And that *cannot* be solved in the current
state of Yacc. The
problem is that there doesn't seem to be *any* solution that
solves both this
problem and the problem of allowing bitwise or's in default
arguments, short of
renaming the bitwise or, which would be such a compatibility
break that it isn't
worth contemplating.
=20
This is at least my understanding of the problem.

Are you saying this from experience? It sounds like it, but
your last statement says not. Don't be so authoritative if you
are not.

Take a look at the parse.y code. Look at the "arg" rule.=20
That's where the action is. All I'm suggesting is that this
entire rule be duplicated and renamed (and all immediate
recursive calls to itself). Maybe "arg2". And then in this
new rule, this alternative would be deleted:

| arg '|' arg
{
/*%%%*/
$$ =3D call_op($1, '|', 1, $3);
/*%
$$ =3D dispatch3(binary, $1, ID2SYM('!'), $3);
%*/
}

Then in the new block args definition, you'd allow optional
arguments (something like f_opt) which would call this "arg2"
instead of "arg".

When I get a chance, I might try this.


__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around=20
http://mail.yahoo.com=20
 
A

Austin Ziegler

Yacc does. Even the simple { |a,b=3D1| a*b } fails because Yacc treats it= as { |
a,b=3D(1| a*b) }. And that *cannot* be solved in the current state of Yac= c. The
problem is that there doesn't seem to be *any* solution that solves both = this
problem and the problem of allowing bitwise or's in default arguments, sh= ort of
renaming the bitwise or, which would be such a compatibility break that i= t isn't
worth contemplating.

This is at least my understanding of the problem.

Is there any particular reason that we can't use a different parser,
e.g., Coco or something else? Surely there's got to be something
better than Yacc by now.

-austin
 
G

gabriele renzi

Austin Ziegler ha scritto:
Is there any particular reason that we can't use a different parser,
e.g., Coco or something else? Surely there's got to be something
better than Yacc by now.

-austin

I remember matz talking about ANTLR long time ago, AFAIR he said
something realted to ANTLR, I think he said that while it could be fast,
thread safe and a lot of nice things there were some problems on the
programmer side (maybe lack of knowledge, I can't remember well).

OTOH I think the people trying to rewrite a ruby parser with COCO had
some problems too (think of ParseTree).
 
P

Phil Tomson

Austin said:
Is there any particular reason that we can't use a different parser,
e.g., Coco or something else? Surely there's got to be something
better than Yacc by now.

While I tend to agree that there are probably much better parser
generators available now like ANTLR or even using a Ruby-based one like
Rockit, I can understand the hesitation to change from yacc. It's a
huge amount of work for one, and there would need to be a lot of
testing to ensure that nothing has broke. I suspect that a change from
yacc to another parser generator would delay Ruby2.0 by at least
several months. Maybe that would be OK. Perhaps there would be other
advantages gained by moving away from yacc that would help justify
doing so?

I'm still in the "It might be a nice feature to have, but it's not
really needed enough to justify the kinds of changes being suggested"
category.

Phil
 
A

Austin Ziegler

While I tend to agree that there are probably much better parser
generators available now like ANTLR or even using a Ruby-based one like
Rockit, I can understand the hesitation to change from yacc. It's a
huge amount of work for one, and there would need to be a lot of
testing to ensure that nothing has broke. I suspect that a change from
yacc to another parser generator would delay Ruby2.0 by at least
several months. Maybe that would be OK. Perhaps there would be other
advantages gained by moving away from yacc that would help justify
doing so?

I'm still in the "It might be a nice feature to have, but it's not
really needed enough to justify the kinds of changes being suggested"
category.

I'm suggesting that it may be time for a parser change because the
proposed language changes IMO are really really really ugly -- and I'd
rather see:

foo =3D lambda { |foo =3D 1 | 2| puts foo }

work than not.

-austin
 
E

Eric Mahurin

--- Austin Ziegler said:
=20
I'm suggesting that it may be time for a parser change
because the
proposed language changes IMO are really really really ugly
-- and I'd
rather see:
=20
foo =3D lambda { |foo =3D 1 | 2| puts foo }
=20
work than not.

How do you interpret the above?

foo =3D lambda { |foo =3D 1| (2|puts foo) }

or

foo =3D lambda { |foo =3D (1|2)| puts foo }

I think the first interpretation is implementable in most
parsers (including yacc, I think) relatively easily if you just
don't allow "|" directly in the default value (unlike a normal
RHS). The second interpretation would be more difficult
because it would require an arbitrary amount of lookahead.=20
Even if the parser can do it, I think arbitrary amounts of
lookahead should be avoided in a language because it will
result in a performance hit (like backtracking in regular
expressions).



=09
=09
__________________________________=20
Yahoo! Mail - PC Magazine Editors' Choice 2005=20
http://mail.yahoo.com
 

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,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top