Default argument values for blocks

C

Christophe Grandsire

En r=E9ponse =E0 Eric Mahurin :
=20
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.
=20

Not experience but this:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/160045
Is that authority enough for you? If matz says it doesn't work, I'd say=20
he knows what he is talking about. It's his language after all, he has=20
spent more time on it than anybody else.
Take a look at the parse.y code.

I wouldn't understand it anyway, I don't know enough C. But when I'm=20
basically repeating what matz says, I do expect that it has some=20
authority, and then I add a disclaimer because I can always=20
misunderstand people. In this case I didn't. What I said was exactly=20
what matz said.
--=20
Christophe Grandsire.

http://rainbow.conlang.free.fr

You need a straight mind to invent a twisted conlang.
 
P

Phil Tomson

Eric said:
How do you interpret the above?

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

or

foo = lambda { |foo = (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.
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).

I'm not a parser expert, but wouldn't the second example you pose be
helped by the fact that you've enclosed the '1|2' expression in parens?
wouldn't that expression within the parens be evaluated first and the
resulting value substituted into the 'foo = ' expression?

I ask because a lot of people have suggested that requiring enclosing
bitwise OR expressions in parens in the block param would solve the
problem.

I don't see any way to do the following without lookahead:

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

And as you say the more lookahead you're doing, the slower the parser
will be.

The goalposts are problematic for this sort of thing. What about an
alternative (some have been suggested):

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

If that one is problematic, then maybe:
lambda{ |(foo = 1|2, bar = foo|3)| puts foo, bar }

This one was proposed long ago as a way to declare a var whose scope is
outside the block:
lambda { <foo = 1|2> puts foo }


But again, I think we should hold out for the 'ideal' syntax or just
forget about the proposed feature.

Phil
 
T

Trans

This one was proposed long ago as a way to declare a var whose scope is
outside the block:

lambda { <foo = 1|2> puts foo }

All sorts of things can work, one that looks quite nice:

lambda { foo = 1|2 -> puts foo }

But I would suggest that everyone give the -> () {} notation chance. I
really hated it at first too, but it has grown on me and I am starting
to like it even more then goal posts b/c of the way it brings the
parameters outside of the block.

T.
 
G

Gyoung-Yoon Noh

Hi,

In message "Re: Default argument values for blocks"
|Hey, what about ":(foo=3D"bar"){puts foo}"? It's still not beautiful but= it's
|still less annoying, and in Ruby we are already used to see colons at th= e
|beginning of a word. Of course, overloading ":" may not be considered a = good
|idea, but it shouldn't be a parsing problem as this one would always be
|followed by a "(" (and that doesn't happen with symbols). And:
|
|collection.each:(foo=3D"bar"){puts foo}
|
|isn't that bad :) .

I have never thought of that. Hmm, let me think about it during the
conference trip. I'm leaving tomorrow morning.

matz.

+1

collection.each : (foo=3D"bar") { puts foo }

It looks like 'when condition : expression' branch in
'case' statement.
 
E

Eric Mahurin

--- Phil Tomson said:
=20

=20
I'm not a parser expert, but wouldn't the second example you
pose be
helped by the fact that you've enclosed the '1|2' expression
in parens?
wouldn't that expression within the parens be evaluated
first and the
resulting value substituted into the 'foo =3D ' expression?

Notice the word "interpretation" in what I said. Those were
the 2 interpretations for that example Austin gave above.



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

David A. Black

Hi --

+1

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

It looks like 'when condition : expression' branch in
'case' statement.

But why would you want a method call and argument list and block to
look like when condition : expression ? There's no "then" meaning to
it.


David
 
R

rue

Aargh, no! :)

Anything but adding syntax. I bet that someone somewhere can either
coax YACC to Do The Right Thing or then re-implement the whole thing
in ANTLR or something. We just need to find that person.

And there we have it, noble knights. Our quest is for the Magical
Parser.
Prepare your mounts, we ride at dawn.
 
D

David A. Black

Hi --

All sorts of things can work, one that looks quite nice:

lambda { foo = 1|2 -> puts foo }

But I would suggest that everyone give the -> () {} notation chance. I
really hated it at first too, but it has grown on me and I am starting
to like it even more then goal posts b/c of the way it brings the
parameters outside of the block.

That's one of the things I don't like about it. Just on a kind of
visceral level, { ... } conveys a much greater sense of a "closure".

Actually, if we must have -> then it would be better to just make it
look more like a method definition, but anonymous:

def x(a,b)
...
end

-> (a,b)
...
end

and just forget about the {}. ->(){} just requires too much visual
parsing and explaining.


David
 
T

Trans

David said:
That's one of the things I don't like about it. Just on a kind of
visceral level, { ... } conveys a much greater sense of a "closure".

Yes, I felt that way as well. But I also sometimes feel like it's
(visually) lopsided.

each {|x| ... }
Actually, if we must have -> then it would be better to just make it
look more like a method definition, but anonymous:

def x(a,b)
...
end

-> (a,b)
...
end

Hmmm, can that be done? That must cause possible ambiguities or why
would we not be using {(x,y)...} already? But then again I know we can
do

def foo(x) do stuff ; end

So I'm not sure.

Matz had also suggested the possibility of ;; for end. Which with your
concept would give.

each -> (x) puts x ;;

I think even uglier. Let's try this:

each -> (x) puts x <-

Oh, yea now were getting somewhere. How about we sub '{' and '}' for
'->' and '<-'? Good idea? ;)

I'm being silly but the point is that you indrectly raise the fair
question of why not

{ (a,b) ... }
and just forget about the {}. ->(){} just requires too much visual
parsing and explaining.

->(){} ?

Oh, you mean empty-block? :)

T.
 
E

ES

Trans said:
David A. Black wrote:




Yes, I felt that way as well. But I also sometimes feel like it's
(visually) lopsided.

each {|x| ... }




Hmmm, can that be done? That must cause possible ambiguities or why
would we not be using {(x,y)...} already? But then again I know we can
do

def foo(x) do stuff ; end

So I'm not sure.

Matz had also suggested the possibility of ;; for end. Which with your
concept would give.

I seem to recall this suggestion was presented on 2005.04.01.
each -> (x) puts x ;;

I think even uglier. Let's try this:

each -> (x) puts x <-

Oh, yea now were getting somewhere. How about we sub '{' and '}' for
'->' and '<-'? Good idea? ;)

I'm being silly but the point is that you indrectly raise the fair
question of why not

{ (a,b) ... }




->(){} ?

Oh, you mean empty-block? :)

No syntax! :)

Damn, we could have *all* learned ANTLR by now.

E
 
D

David A. Black

Hi --

Read the : as "do". When x is one, do... For every item of the
collection, do...

But it's such a stretch to look at it that way (and it's not the same
"do" as the usual "do").
I think the :(){} stuff is the nicest looking approach as of now, but
I wouldn't use it when ordinary {||} is enough.

As I said previously, I think that if there's a block syntax with (),
it should *not* have {}. There's simply no reason for {} at that
point, and all it does is make the () feel like it's in the wrong
place.

I still just can't imagine having two ways to do all of this -- trying
to explain it, reconcile it with other syntax, etc. It's bad enough
that people debate things like using empty parens on method calls, or
whether to omit "return".... The thought of people having to learn
two block syntaxes, and choosing between them, and debating it (which
*will* happen, at great length), all because of the stupid pipe
character.... oy vey.

Oh well, back to RubyConf preparation :)


David
 
A

Ara.T.Howard

Read the : as "do". When x is one, do... For every item of the
collection, do...

I think the :(){} stuff is the nicest looking approach as of now, but
I wouldn't use it when ordinary {||} is enough.

it all seems like a __lot__ of coding for someone when one can simply

harp:~ > cat a.rb
collection = [ 4, nil, 2 ]
collection.each{|foo| foo ||= "bar"; puts foo }

harp:~ > ruby a.rb
4
bar
2

by my reckoning that's only a few xtra chars

if one wanted to do that alot you can just

harp:~ > cat a.rb
class Array
def merge *mask
zip(mask.flatten).map{|v| v.first or v.last}
end
def merge! *mask
replace merge(*mask)
end
end

table =
[
[nil, 4, 2],
[4, nil, 2],
[4, 2, nil],
]

table.each do |row|
row.merge! defaults = %w(a b c)
p row
end

harp:~ > ruby a.rb
["a", 4, 2]
[4, "b", 2]
[4, 2, "c"]


imho

table.each do |row|; row.merge! %w(a b c)
p row
end

is very nearly as clear and as concise as

table.each do |x = 'a', y = 'b', z = 'c'|
row = [x, y, z]
p row
end

and this extends to multi-parm blocks just as easily

assoc_array.each do |k = 'no key', v = 'no val'|
p [k, v]
end

assoc_array.each do |kv|; kv.merge! 'no key', 'no val'
p kv
end

i guess i'm just thinking block parameters, and method parameters, are always
lists (possibly empty) and that a nifty new list operator could solve this
with even saying yac...

regards.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
A

Austin Ziegler

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 }

The first isn't a reasonable interpretation. (Why? Execute it
mentally; we "puts foo", and puts returns +nil+; 2 | nil is a
TypeError.) The second is by far the bettter interpretation -- and
it's a lot cleaner than ->(foo =3D 1 | 2) { puts foo } will *ever* be.
I'd much rather put up with a performance hit than that ....
trainwreck in Ruby.

-austin
 
A

Austin Ziegler

All sorts of things can work, one that looks quite nice:

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

But I would suggest that everyone give the -> () {} notation chance. I
really hated it at first too, but it has grown on me and I am starting
to like it even more then goal posts b/c of the way it brings the
parameters outside of the block.

1. I don't use 1.9 at this point. It doesn't yet have the features
that I need that will make it important for me to use.
2. The ->(){} syntax is a trainwreck. It's a Russian military
manoeuver. It's Pompeii. I fear that I won't *ever* like it; the
parameters belong *inside* the block, IMO. Strictly speaking, the
parameters to a method are *inside* the method definition (they're
contained within the def/end pair). Why should blocks be any
different?

My main issue with ->(){} is that I had to hold down the shift key for
the last five characters. I don't have to hold down the shift key that
often for most of what I do with Ruby.

-austin
 
T

Trans

Ara.T.Howard said:
Read the : as "do". When x is one, do... For every item of the
collection, do...

I think the :(){} stuff is the nicest looking approach as of now, but
I wouldn't use it when ordinary {||} is enough.

it all seems like a __lot__ of coding for someone when one can simply

harp:~ > cat a.rb
collection = [ 4, nil, 2 ]
collection.each{|foo| foo ||= "bar"; puts foo }

harp:~ > ruby a.rb
4
bar
2

by my reckoning that's only a few xtra chars

if one wanted to do that alot you can just

harp:~ > cat a.rb
class Array
def merge *mask
zip(mask.flatten).map{|v| v.first or v.last}
end
def merge! *mask
replace merge(*mask)
end
end

table =
[
[nil, 4, 2],
[4, nil, 2],
[4, 2, nil],
]

table.each do |row|
row.merge! defaults = %w(a b c)
p row
end

harp:~ > ruby a.rb
["a", 4, 2]
[4, "b", 2]
[4, 2, "c"]


imho

table.each do |row|; row.merge! %w(a b c)
p row
end

is very nearly as clear and as concise as

table.each do |x = 'a', y = 'b', z = 'c'|
row = [x, y, z]
p row
end

and this extends to multi-parm blocks just as easily

assoc_array.each do |k = 'no key', v = 'no val'|
p [k, v]
end

assoc_array.each do |kv|; kv.merge! 'no key', 'no val'
p kv
end

i guess i'm just thinking block parameters, and method parameters, are always
lists (possibly empty) and that a nifty new list operator could solve this
with even saying yac...

I don't think we should be relegated to these kinds of cleverness. It
is desireable to have lamda and def have the same basic semantics.

OTOH, It would seem in a manner that you are suggesting perhaps we go
the other way and get rid of default parameters altogeter and use more
explict means, eg. psuedo-code:

def foo(?a)
a = 1 if a.nack?
end

Don;t dismiss the idea so readily either. It does have merit, since
default parameters are not at all fullproof. No doubt you, like myself
often HAVE to do something like the above in many cases to prevent
certain "bad" arguments getting thru. Default parameters can actually
encourage poor management of parameters. So maybe this is a good idea.
And then the whole need for ->(){} would just go away.

T.
 
A

Ara.T.Howard

I don't think we should be relegated to these kinds of cleverness.

it's not unlike the cleverness of

method k => v

automagiacally working though is it. i appreciate the sentiment however.

It is desireable to have lamda and def have the same basic semantics.

i totally agree - but that would mean

- def acts as closure. nothing can now be garbage collected. or lambda does
not act as closure, lambda is useless without explicit var 'closing'
abilities or local variable/closure definition similar to the way thread
works.

- def returns an object

- lambda methods can be defined to accept a block, or def ceases to.
neither seesm plausible

etc. etc.

in short it seems that merging lambda with def implies a totaly revamping of
ruby into more functional language and that implies a mountain of work, and
for what? only a little its seems. the in-between state where ruby lies now
is actually the most practical of places to be.
OTOH, It would seem in a manner that you are suggesting perhaps we go the
other way and get rid of default parameters altogeter and use more explict
means, eg. psuedo-code:

def foo(?a)
a = 1 if a.nack?
end

Don;t dismiss the idea so readily either. It does have merit, since default
parameters are not at all fullproof. No doubt you, like myself often HAVE to
do something like the above in many cases to prevent certain "bad" arguments
getting thru.

i'm open to something akin to that. that's why i wrote parseargs
(http://www.codeforpeople.com/lib/ruby/parseargs/). note that it works just
fine with either methods OR lambda:

harp:~ > cat a.rb
require 'parseargs'
include ParseArgs

def method(*a)
pa = parseargs(a) {
required_argument :a
optional_argument :b => 2
}
a, b = pa.a, pa.b
p [a, b]
end

method 4
method 4,2

method = lambda do |*a|
pa = parseargs(a) {
required_argument :a
optional_argument :b => 2
}
a, b = pa.a, pa.b
p [a, b]
end

method[ 4 ]
method[ 4,2 ]


harp:~ > ruby a.rb
[4, 2]
[4, 2]
[4, 2]
[4, 2]

Default parameters can actually encourage poor management of > parameters.

i agree. i almost never use them. keywords are much, much, much better. i
detest the look of

render 42, true, false
render 42, false

huh!? where are my docs? where is the code? (clock ticks...)

render 42, 'visible' => true, 'badly' => false
render 42, 'visible' => false

ahhhh - much better. my take is that arguments are __required__. period.
anything else is a keyword.

the only exceptions are those cases where a paramter is the default 99% of the
time and a keyword is overkill. for example

def log msg, io = STDOUT
end

but even here this it's perhaps even more an argument to say, instead

log '42', 'io' => STDERR

since the default arg is used so seldom it nearly always causes whomever is
reading the code to need to consult the docs. it's especially bad when people
code things like

def log msg, io = STDOUT, indent = 0
end

and we're forced to write

log '42', STDOUT, 7

which is redundant and obscure compared to

log '42', 'indent' => 7
log '42', 'indent' => 7, 'io' => STDERR

So maybe this is a good idea. And then the whole need for ->(){} would just
go away.

that's the point - it isn't 'needed'. unification is a great idea - but's
it's so much bigger that this thread it isn't even funny. until then, imho,
some new methods are in order.

kind regards.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| anything that contradicts experience and logic should be abandoned.
| -- h.h. the 14th dalai lama
===============================================================================
 
S

Sean O'Halpin

Nice!

Regards,
Sean
Or even:

class Array
def merge *mask
mask.flatten.zip(self).map{|v| v.last or v.first}
end
end

so that
[1,2].merge:)a, :b, c:) #=3D> [1,2,:c]

Regards,

Sean
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Default argument values for blocks"

|For instance_eval, how about simply
|
| instance_eval(*args, &block)
|
|i.e. if it has a block, then the arguments are passed to it.
|e.g.
|
| instance_eval(1,2,3) {|a,b,c| ...}
| instance_eval(1,2,3, &block)

I will supply something behave like this, but with a different name
from instance_eval. I'm thinking of instance_exec() right now.

matz.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Default argument values for blocks"

|I don't mean to jump on your idea specifically. The whole thread
|seems to be about which arbitrary bit of punctuation might be least
|ugly. I'm totally unconvinced that default values for block variables
|are worth doing any of them.

The point is not providing default values for block parameters, but
something with true argument passing behavior, which is right now
emulated by multiple assignment.

matz.
 

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,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top