A comparison by example of keyword argument styles

D

Daniel Schierbeck

This is just to get the discussion focused. With named arguments, I'm
thinking something along the line of this:

An assignment symbol of some sort ("=", ":", "=>", "->", etc.), with a
key on the left side and a value on the right side.

key: value
key = value
:key = value
key => value
key -> value

Furthermore, the named arguments are gathered in a hash in which the
keys are symbols.

keys[:key] -> value

We're discussing two things: what assignment symbol to use and what the
key should look like (although the latter doesn't seem to be causing
much trouble). I'll just go through each of the styles suggested above
and pose my thoughts on them.


key -> value

def foo(a -> "default", **keys)
foo(a -> "foobar")

con: I don't think we want to add another arrow to the language.
No way to specify a named argument without a default value.


key => value

def foo(a => "default", **keys)
foo(a => "foobar")

pro: It's what being used at the caller-level already. It looks cool,
even when the value's a symbol: foo(a => :b).

con: To me, => means "points to", not "is". Therefore it might not be
good as an assignment symbol. No way to specify a named argument
without a default value.


key: value

def foo(a:, b: "default", **keys)
foo(a: "foobar")

pro: Already used in other languages. Intuitive in many cases:
obj.move from: NewYork, to: Paris

con: Looks silly when the value is a symbol:
obj.move from: :newyork, to: :paris
mandatory named arguments look silly.


:key = value

def foo:)a, :b = "default", **keys)
foo:)a = "foobar")

pro: The key is a symbol, which seems consistent with it being it also
in the key hash. Looks cool even when the value is a symbol.

con: Looks like you're assigning a value to a symbol.


key = value

foo(a = "foobar")

pro: Looks clean and cool, even when the value is a symbol.

con: May be hard to implement. Doesn't specify how you seperate named
and positional arguments (maybe use a keyword like "named"?).
def foo(a, b = "bar", named c = "default", **keys)


Personally I prefer the last version (key = value) with a keyword in
front of named arguments. Furthermore, the key: value syntax could be
used alongside, where appropriate, on the caller side.

def foo(a, b = "bar", named c, named d = "default", **keys)
foo(1, "foo", c = 123)
foo(1, "foo", c: 123)



Just my $.02

Cheers,
Daniel
 
T

Trans

Daniel said:
key => value

def foo(a => "default", **keys)
foo(a => "foobar")

pro: It's what being used at the caller-level already. It looks cool,
even when the value's a symbol: foo(a => :b).

con: To me, => means "points to", not "is". Therefore it might not be
good as an assignment symbol. No way to specify a named argument
without a default value.

There's no reason you can't leave the value off.

def foo(a=>) # same as foo(a=>nil)

I'm not saying its great. But it would work. And the pros for this are
pretty signifficant.

The 'key: :sym' effect is very unfortunate. I use symbol argument in a
number of places. Whether I will use them with keys much, who knows,
but given the problem I probably just won't and may even end up only
using => so as to avoid it. Then the ':' may as well not exist. I don't
know. But it sure would be nice if there was a simple fix to this.
Perhaps if backticks can become an *additional* way to express a
symbol? I know they are currently used for a type of system call, but
I'm sure a regular method would be okay for that. And I don't think
that's too much to ask of Ruby 2.0. Seems like a reasonble solution.

Of course there's still the question of Evan vs. Matz keyword
arguments. I can go either way. I think we all pretty much can go
either way. We know one works fine for Python, we know the other works
fine for Lisp. Is one truly better than the other? Doesn't seem like it
--at least not by much. Mostly, they're just different. Pick one.

T.
 
N

Nikolai Weibull

Daniel said:
key =3D value
=20
foo(a =3D "foobar")
=20
pro: Looks clean and cool, even when the value is a symbol.
=20
con: May be hard to implement. Doesn't specify how you seperate named
and positional arguments (maybe use a keyword like "named"?).
def foo(a, b =3D "bar", named c =3D "default", **keys)

It=E2=80=99s impossible to implement without breaking the current semanti=
cs of
the assignment operator.

To me, =E2=80=9Ca =3D "foobar"=E2=80=9D reads very much like an assignmen=
t, not as an
argument specifier, but one mans opinion is more or less irrelevant.

If everyone can just get around the fact that passing symbols using
keyword arguments will look a bit awkward, with the two colons in a row
separated by a space, I really don=E2=80=99t see what the problem is. Fo=
r every
other case, I think that <symbol>':'<ws>+<expr> reads a lot better than
<symbol><ws>*'=3D'<ws>*<expr>, or

=E2=8B=AE
ary.grep(/regex/, start: 5, end: 8)
=E2=8B=AE

over

=E2=8B=AE
ary.grep(/regex/, start =3D 5, end =3D 8)
=E2=8B=AE

Reading the latter, I find my brain wondering where start and end are
being used in the code preceding and following the call to
Enumerable#grep. In the earlier I don=E2=80=99t get the feeling that =E2=
=80=9Cstart=E2=80=9D
and =E2=80=9Cend=E2=80=9D are variables that are getting values assigned =
to them.

nikolai

--=20
Nikolai Weibull: now available free of charge at http://bitwi.se/!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}
 
R

Ryan Leavengood

T24gMTAvMjMvMDUsIE5pa29sYWkgV2VpYnVsbAo8bWFpbGluZy1saXN0cy5ydWJ5LXRhbGtAcmF3
dW5jdXQuZWxpdGVtYWlsLm9yZz4gd3JvdGU6Cj4g4ouuCj4gYXJ5LmdyZXAoL3JlZ2V4Lywgc3Rh
cnQ6IDUsIGVuZDogOCkKPiDii64KPgo+IG92ZXIKPgo+IOKLrgo+IGFyeS5ncmVwKC9yZWdleC8s
IHN0YXJ0ID0gNSwgZW5kID0gOCkKPiDii64KPgo+IFJlYWRpbmcgdGhlIGxhdHRlciwgSSBmaW5k
IG15IGJyYWluIHdvbmRlcmluZyB3aGVyZSBzdGFydCBhbmQgZW5kIGFyZQo+IGJlaW5nIHVzZWQg
aW4gdGhlIGNvZGUgcHJlY2VkaW5nIGFuZCBmb2xsb3dpbmcgdGhlIGNhbGwgdG8KPiBFbnVtZXJh
YmxlI2dyZXAuICBJbiB0aGUgZWFybGllciBJIGRvbid0IGdldCB0aGUgZmVlbGluZyB0aGF0ICJz
dGFydCIKPiBhbmQgImVuZCIgYXJlIHZhcmlhYmxlcyB0aGF0IGFyZSBnZXR0aW5nIHZhbHVlcyBh
c3NpZ25lZCB0byB0aGVtLgoKSSBhZ3JlZS4gQXQgbGVhc3QgYm90aCB0aGUgU3lkbmV5IGFuZCBN
YXR6IHRlY2huaXF1ZXMgZm9yIGtleXdvcmQKYXJndW1lbnRzIHVzZSB0aGUgY29sb24uIEkgZG8g
bm90IHRoaW5rIHVzaW5nIGVxdWFscyB3aWxsIGV2ZXIgYmUgYQp2YWxpZCBvcHRpb24uIFJ1Ynkg
aXNuJ3QgUHl0aG9uLCBhbmQgdGhhbmsgZ29vZG5lc3MgZm9yIHRoYXQuCgpSeWFuCg==
 
N

Nikolai Weibull

Trans said:
The 'key: :sym' effect is very unfortunate. I use symbol argument in a
number of places.

Yes, it=E2=80=99s a bit ugly, but to me, using =3D> for key-value pairs i=
s not
visually pleasing either. However, I use them all the time when
creating hashes. How about =E2=80=98=E2=87=92=E2=80=99?
Whether I will use them with keys much, who knows, but given the
problem I probably just won't and may even end up only using =3D> so as
to avoid it. Then the ':' may as well not exist.

What, we shouldn=E2=80=99t have =E2=80=98:=E2=80=99 since you personally =
will never use it?

If you don=E2=80=99t know how you will be using keyword arguments, then h=
ow can
you know if the proposed syntax will cause a visually displeasing
result? As I said in my other mail on this sub-thread, I think that
we=E2=80=99re making too much of the =E2=80=9Cbut symbol arguments to key=
word parameters
will look ugly=E2=80=9D issue.
I don't know. But it sure would be nice if there was a simple fix to
this. Perhaps if backticks can become an *additional* way to express
a symbol? I know they are currently used for a type of system call,
but I'm sure a regular method would be okay for that. And I don't
think that's too much to ask of Ruby 2.0. Seems like a reasonble
solution.

How about "symbol".to_sym? Personally, I=E2=80=99d love to see Unicode b=
eing
used in Ruby=E2=80=99s syntax (OK, I know that it won=E2=80=99t happen, b=
ut as long as
we=E2=80=99re on this rather pointless discussion):

=E2=80=A2 =C2=ABsymbol=C2=BB
=E2=80=A2 =C2=BBsymbol=C2=AB
=E2=80=A2 =C2=BBsymbol
=E2=80=A2 =E2=9F=A8symbol=E2=9F=A9
Of course there's still the question of Evan vs. Matz keyword
arguments.=20

Named arguments versus keyword arguments, to be more precise, as they
are two different things. Or have I missed something?
I can go either way. I think we all pretty much can go either way. We
know one works fine for Python, we know the other works fine for Lisp.
Is one truly better than the other? Doesn't seem like it --at least
not by much. Mostly, they're just different. Pick one.

They are two different things. Keyword arguments are a cleaner way to
implement methods that take optional parameters. Instead of writing a
method like

def m(a, b, c =3D 1)
=E2=8B=AE
end

we can write it as

def m(a, b, c: 1)
=E2=8B=AE
end

and then at invocation we write

m(1, 2, c: 3)

instead of

m(1, 2, 3)

to make it clear that we are passing an argument to a parameter that is
usually optional.

Named arguments is just a way of naming the parameter to tie the given
argument to.

They seem to be the same thing, but in reality they aren=E2=80=99t. Pers=
onally,
I don=E2=80=99t see the point of naming arguments, as parameters are usua=
lly
ordered in a sensible way already (i.e., travel(from, to) is clear
enough; travel(from: from, to: to) doesn=E2=80=99t add anything). I do,
however, see a point of being able to clearly state what optional
(keyword) parameters I do care about, as this allows me to skip defining
those that I don=E2=80=99t care about:

def m(a, b, c: 1, d: 2)
=E2=8B=AE
end

m(1, 2, d: 4)

nikolai

--=20
Nikolai Weibull: now available free of charge at http://bitwi.se/!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}
 
E

Eric Mahurin

--- Daniel Schierbeck said:
Furthermore, the named arguments are gathered in a hash in
which the=20
keys are symbols.
=20
keys[:key] -> value

I don't think this should be an assumption about how named
arguments work (unless you are talking about the final **keys
arg). The easiest initial implementation may be to use hashes
to pass around named arguments, but as we get better VM's and
compilers, that could change.

The main thing I don't like about the current proposal (what
matz presented at rubyconf) is that the *args (remaining
positional args) also contains the named arguments. So this:

def foo(a,b=3D1,*args,c:,d:2,**keys) ... end

called like this:

foo("a","b","x","y",c:"c",e:"e")

gives:

a =3D "a"
b =3D "b"
args =3D ["x","y",{ :c =3D> "c", :d =3D> 2, :e =3D> "e" }]
c =3D "c"
d =3D 2
keys =3D { :e =3D> "e" }

What use is it to have the named arguments appear twice? I
understand the need if you don't have named arguments in the
definition (compatibility), but as soon as named arguments are
specified, it seems to serve no purpose.

Also, like we have array splatting when you call a method, I
think hash splatting would also be useful. matz said that
hashes would be automatically splatted if they were the last
argument, but this results in ambiguity:

def foo(a,b=3D{},c:2,**keys) ... end

foo("a",{:x =3D> 1,:y =3D> 2})

Does the above result in:

a =3D "a", b =3D {:x =3D> 1,:y =3D> 2}, c =3D 2, keys =3D {}

or:

a =3D "a", b =3D {}, c =3D 2, keys =3D {:x =3D> 1,:y =3D> 2}

I would rather see it be the first interpretation and if you
wanted the second, you'd "splat" the hash:

foo("a",**{:x =3D> 1,:y =3D> 2})

which would be the same as:

foo("a", x : 1, y : 2)


I think whether we have something sydney/python like where
positional arguments can automatically be named or whether
there is a clear distinction between positional/named like
lisp, it is OK. Another language comparison to make is just
the shell command-line. One the command-line named arguments
are effectively the "options" and positional are the rest. On
that note, another option would be to specify the named
arguments like command-line options:

def foo(a, b=3D1, --c=3D2, --*keys) ... end

foo("a", --c=3D"c", --x=3D1, --y=3D2)

Or simply "-" instead of "--" if it could be handled in the
parser (could be confused with a unary - at first). But, I'm
not terribly concerned with syntax.

I'm hoping that we have enough reflection for these named
arguments like #arity. I'm thinking that with named arguments
a potential simple application would be as a spec for handling
command-line options (named arguments). I'd like to see all of
the keywords and defaults accessible from Proc/Method objects.=20
It'd be nice to have access to defaults of positional args
while wer'e asking.

Since Ruby 2 will also be completely redoing how
multiple-values are handled, why not also consider being able
to name your return values? If a method returns a bunch of
attributes, it would be nice for the caller to be able to pick
what they want.




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

Yukihiro Matsumoto

Hi,

In message "Re: A comparison by example of keyword argument styles"

|What use is it to have the named arguments appear twice? I
|understand the need if you don't have named arguments in the
|definition (compatibility), but as soon as named arguments are
|specified, it seems to serve no purpose.

It's for method delegation. Currently we do

def foo(*args)
bar(*args)
end

for method delegation. Under my proposal, this delegation would still
work fine, since we have named arguments appear twice, otherwise we
must change the code as

def foo(*args, **keys)
bar(*args, **keys)
end

everywhere, to just do delegation.

|Also, like we have array splatting when you call a method, I
|think hash splatting would also be useful. matz said that
|hashes would be automatically splatted if they were the last
|argument, but this results in ambiguity:
|
|def foo(a,b={},c:2,**keys) ... end
|
|foo("a",{:x => 1,:y => 2})
|
|Does the above result in:
|
|a = "a", b = {:x => 1,:y => 2}, c = 2, keys = {}
|
|or:
|
|a = "a", b = {}, c = 2, keys = {:x => 1,:y => 2}

The former. Keyword argument uses the last remaining hash.

|I'm hoping that we have enough reflection for these named
|arguments like #arity. I'm thinking that with named arguments
|a potential simple application would be as a spec for handling
|command-line options (named arguments). I'd like to see all of
|the keywords and defaults accessible from Proc/Method objects.
|It'd be nice to have access to defaults of positional args
|while wer'e asking.

Accepting keywords should be obtained from some kind of reflection
API, although the API is not fixed yet. But I'm not sure default
values can be accessed since they are arbitrary expression, and we
have no good way to represent pre-evaluated expression in Ruby (unlike
in Lisp, where we can use S expression).

|Since Ruby 2 will also be completely redoing how
|multiple-values are handled, why not also consider being able
|to name your return values? If a method returns a bunch of
|attributes, it would be nice for the caller to be able to pick
|what they want.

I have no good idea of multiple value binding for named values
(i.e. syntax). Do you?

matz.
 
T

Trans

Yukihiro said:
It's for method delegation. Currently we do

def foo(*args)
bar(*args)
end

for method delegation. Under my proposal, this delegation would still
work fine, since we have named arguments appear twice, otherwise we
must change the code as

def foo(*args, **keys)
bar(*args, **keys)
end

everywhere, to just do delegation.

This is sinking worse and worse into a stenchy quagmire. If someone
puts **keys in the parameters then it makes sense for the keys NOT to
appear in args. I know you want to remain backward compatable so
without **keys then the keys will appear in args. But how does one say
they want any number of ordered parameters but NO named parameters? I
suppose you just can't. Moreover the args won't be cleanly separated.
So this doesn't do us much good, I'll still be pop'n off trailing
hashes :( Really, think about that!

---

Thinking about reusing named parameters vs. adding key arguments
(better?) I recant on my previous statement of their being little
difference. The later does have one clear disadvantage: it requires a
choice --which parameters will be fixed and which will be keyed. Since
you can't have it both ways, it's really going to tie you down.
Consider this scenario:

You have a method already

def foo(a,b=1,c=2)

Now it's been a bother b/c you want to sometimes call it as foo(a,c).
But you can't. To get around, you've done what we've all done at one
point or another, created an extra method

def foo2(a,c=2,b=1)

Now along comes matz' keyd args. Does it help? Can you deprecate foo2
which you never really wanted in the first place and use?

def foo(a,b:1,c:2)

You can but, ugh, that's serious backward compatabilty breakage!!! So
what to do? Do you add YAM (yet another method) #foo3 for this? Do you
get tricky with it and do

def foo(a,b=1,c=2,b2:1,c2:2)
b = b || b2
c = c || c2

Well a little better, but now you have TWO DIFFERENT names for each
parameter and you could have just as well done this with a trailing
hash. The keyed args didn't buy you much of anything. Nope, it just
doesn't do us much good.

Buy let's say instead, here comes along the named parameters approach
like Sydney's. What happens? Easy peasy. Deprecate #foo2 (add a
warning) and your done. You can start using #foo in the new way
'foo(a,c:x)' as it suits you and backward compatability is maintained.

---

In anycase case one of the nice things about named args, should you
with to change a parameter name, you can offer both during a transition
period, in fact you may want to offer a few alternatives anyway to make
usage a bit more fluid.

def foo( beer:, beers:, beer_bottles: )
b = beer || beers || beer_bottles

A way to specify an OR in the signiture would be nice with something
like this too. Say:

def foo( beer: | beers: | beer_bottles: )
b = beer # doesn't matter which.

But again the problem that starts to arise here are _very large
signitures_. And again to me this indicatates a real need for some type
of Signture class one can use to define them and plug them in where
needed, even reuse them. Such an object would give ultimate reflexion
too.

---

Just some comparisions:

foo:)foo=>:do, :bar=>:re, :baz=>:mi) # today


foo(foo=>:do, bar=>:re, baz=>:mi) # minor adj.


foo(foo: :do, bar: :re, baz: :mi) # colon notation


foo(foo:`do, bar:`re, baz:`mi) # alt. symbol

---

BTW, those of you who do get me --I'm not trying to make some major
coherent argument. I'm just pointing out various vantages.

T.
 
A

Ara.T.Howard

This is sinking worse and worse into a stenchy quagmire. If someone puts
**keys in the parameters then it makes sense for the keys NOT to appear in
args. I know you want to remain backward compatable so without **keys then
the keys will appear in args. But how does one say they want any number of
ordered parameters but NO named parameters? I suppose you just can't.
Moreover the args won't be cleanly separated. So this doesn't do us much
good, I'll still be pop'n off trailing hashes :( Really, think about that!

it's not that big a deal to do is it? i've abstracted this in alib like so:

harp:~ > cat a.rb
require 'alib'
include ALib::Util

def meth *argv
args, opts = optfilter argv
foo = getopt 'foo', opts
bar = getopt 'bar', opts, 'forty-two' # default value

p 'args' => args
p 'opts' => opts
p 'foo' => foo
p 'bar' => bar
end

meth 'foo', 'bar', 'foo' => 42


harp:~ > ruby a.rb
{"args"=>["foo", "bar"]}
{"opts"=>{"foo"=>42}}
{"foo"=>42}
{"bar"=>"forty-two"}


easy cheasy. parseargs abstracts even further. if a language mod must be
made the answer seems clear: old style keywords land in *args as before, new
style ones land in keys, for example:

def meth *args
p args
end

meth 42 #=> [42]
meth 42, 'foo' => 42 #=> [42, {'foo' => 42}]


def meth *args, **keys
p [args, keys]
end

meth 42 #=> [ [42], {} ]
meth 42, 'foo' => 42 #=> [ [42, {'foo' => 42}], {} ]
meth 42, foo : 42 #=> [ [42, {}], {'foo' => 42} ]
meth 42, 'foo' => 42, foo : 42 #=> [ [42, {'foo' => 42}], {'foo' => 42} ]

i might add that i hope whatever keys object will hash key and symbol
arguments the same so we can do

val = keys['foo']

or

val = keys[:foo]

i hate having to check for both (that's what getopt above does).

2cts.

-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
===============================================================================
 
T

Trans

Ara.T.Howard said:
it's not that big a deal to do is it? i've abstracted this in alib like so:

harp:~ > cat a.rb
require 'alib'
include ALib::Util

def meth *argv
args, opts = optfilter argv
foo = getopt 'foo', opts
bar = getopt 'bar', opts, 'forty-two' # default value

p 'args' => args
p 'opts' => opts
p 'foo' => foo
p 'bar' => bar
end

meth 'foo', 'bar', 'foo' => 42

harp:~ > ruby a.rb
{"args"=>["foo", "bar"]}
{"opts"=>{"foo"=>42}}
{"foo"=>42}
{"bar"=>"forty-two"}


easy cheasy. parseargs abstracts even further.

That's nice, but then why would we need key args if we'd do this
anyway?
if a language mod must be
made the answer seems clear: old style keywords land in *args as before, new
style ones land in keys, for example:

def meth *args
p args
end

meth 42 #=> [42]
meth 42, 'foo' => 42 #=> [42, {'foo' => 42}]


def meth *args, **keys
p [args, keys]
end

meth 42 #=> [ [42], {} ]
meth 42, 'foo' => 42 #=> [ [42, {'foo' => 42}], {} ]
meth 42, foo : 42 #=> [ [42, {}], {'foo' => 42} ]
meth 42, 'foo' => 42, foo : 42 #=> [ [42, {'foo' => 42}], {'foo' => 42} ]
From what I understand this isn't what will happen, but rather
meth 42, foo : 42 #=> [ [42, {:foo => 42}], {:foo => 42} ]
and

meth 42, 'foo' => 42, foo : 42 #=> Error
i might add that i hope whatever keys object will hash key and symbol
arguments the same so we can do

val = keys['foo']

or

val = keys[:foo]

i hate having to check for both (that's what getopt above does).

Interesting point. Perhaps better then:

val = keys.foo

Would go even better with Evan's mixed parameter object.

def(*parms)
p parms
p parms.named
p parms.foo
end

foo( 1,2,foo:3 )
=> [1,2]
{:foo=>3}
3

T.
 
E

ES

it's not that big a deal to do is it?

Yeah, but this just seems like fixing the symptoms of a problem
rather than the root cause. I have to say, the further this goes
on, the less I like the Lispish syntax for the sheer convolutedness.
Lisp did things the way it did because of its implementation an
possibly limitations ruby does not suffer from.

Also, and please enlighten me here, I have seen no tangible payback
for the added complexity except for being able to opt out of using
keyword arguments altoghether (Sydney's model creating them implicitly).
Am I missing something? I do not think there is any advantage in
delegation (when implemented as I posted earlier).
<snip example>

-a

E
 
A

Ara.T.Howard

That's nice, but then why would we need key args if we'd do this anyway?

that was my point ;-) just wondering out loud how important lanuage support
greater than collecting open key/values pairs is...
From what I understand this isn't what will happen, but rather
meth 42, foo : 42 #=> [ [42, {:foo => 42}], {:foo => 42} ]
eek!
meth 42, 'foo' => 42, foo : 42 #=> Error
eeeeek!
i might add that i hope whatever keys object will hash key and symbol
arguments the same so we can do

val = keys['foo']
or

val = keys[:foo]

i hate having to check for both (that's what getopt above does).

Interesting point. Perhaps better then:

val = keys.foo

i do like

verbose = keys.value_at(%w( verbose verbosity v )).first

eg - a hash interface.
Would go even better with Evan's mixed parameter object.

def(*parms)
p parms
p parms.named
p parms.foo
end

foo( 1,2,foo:3 )
=> [1,2]
{:foo=>3}
3

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

def meth *argv
pa = parseargs(argv){ args %w( a b ) and kws %w( c d ) }
p pa.arguments.map{|a| a.name}
p pa.keywords.map{|k| k.name}
p pa.a
p pa.b
p pa.c
p pa.d
end

meth 4, 2, 'c' => 'forty', 'd' => 'two'


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


;-)

-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
===============================================================================
 
G

gga

Yukihiro said:
Hi,

Right syntax is

def function(a, b, c, d:, e: 1, f:)
end

So if I have default values on both positional and named parameters, it
would look like this?

def function( a, b = 2, c = 1, d: 1, e: 2, f: 3)
end

Hmm...

The reason why pythonist don't need the distinction between
named/positional parameters is that exposing an api also comes down to
common sense. Let's see if an example works as intended.
To make sure it does, I *won't* say which parameters I expect to be
positional parameters in my api and which one's I already locked. I
won't provide any rdocs, either. Let's see if you guess...

def texture( name, smin, tmin, smax, tmax,
filter = 'gaussian', filtersize = 0.5 )
end

Looking at the above, which parameters would you call by name and which
ones by position? Spoiler below...
















If you said filter and texture as the named parameters, python's method
probably is on to something.

IMO, parameter default values are so very tighly linked to named
parameters that I almost see no distinction most of the time. I often
don't see positional arguments with defaults. The above function, btw,
is not taken from python. It is taken from probably the oldest api in
existance that has never had a dramatic syntactic change (and where I
first saw what named parameters could do): Renderman. This 3d
shading language api has lasted over 20 years without a change and
afaik, they were the first or one of the first languages to have named
parameters.
And yes, before someone points it out, the texture() call in renderman
is more complex than the above, as it automatically reads from globals
and also works with a single parameter and worse, it is overloaded
based on its return values, making it also a perfect counter-example to
the idea of the rule of positional parameters not having defaults.
Let's see how a python guy would solve this issue...

def texture( name, smin = $s, tmin = $t, smax = $s + $ds, tmax =
$s + $dt,
# named parameters:
filter = 'gaussian', filtersize = 0.5 )

I added only a single comment... in the middle of the function
definition.
Do I really need the interpreter to enforce the above and have to
rewrite all previously written functions to support named parameters
now?
 
G

gga

Nabu said:
def function(a, b, c = "" ; d, e = 1, *f)
end

Much nicer than what I suggested, for sure. The semicolon is already a
separator in ruby... so there's at least some logic to the symbol.
From a readability stand-point, having something like:

- Definition:

def function(a, b, c = ""; d, e = 1, *f)
end

- While Invokation is Matz-like:

function 1, 3, "hello", e: 20

...is certainly very readable.

Most definitively not symetrical, thou.
 
D

Daniel Schierbeck

How about this definition syntax (note that `=>' is just being used here
as an example):

def foo(a, b = 1, named c, named d = 2, *args, **keys)
# notice that c and d are local vars
puts a, b, c, d
end

`keys' could also include c and d. `foo' could then be called like this:

foo(1, 2, c => 3, d => 4, e => 5)
foo(1, 2, 3, 4, e => 5)

keys -> {:c => 3, :d => 4, :e => 5}

This would also solve the problem with passing on an argument list,
since named arguments would naturally appear in the `*args' array:

def foo(*args)
bar(*args)
end

foo(1, 2, a => 3, b => 4) -> args = [1, 2, 3, 4]

Only problem is that you can't make your method callable with named
arguments only.


Cheers,
Daniel
 
E

Eric Mahurin

--- Yukihiro Matsumoto said:
Hi,
=20
In message "Re: A comparison by example of keyword argument
styles"
on Mon, 24 Oct 2005 05:50:39 +0900, Eric Mahurin
=20
|What use is it to have the named arguments appear twice? I
|understand the need if you don't have named arguments in the
|definition (compatibility), but as soon as named arguments
are
|specified, it seems to serve no purpose.
=20
It's for method delegation. Currently we do=20
=20
def foo(*args)
bar(*args)
end
=20
for method delegation.

To do the full delegation right now you really need:

def foo(*args, &block)
bar(*args, &block)
end
Under my proposal, this delegation
would still
work fine, since we have named arguments appear twice,
otherwise we
must change the code as
=20
def foo(*args, **keys)
bar(*args, **keys)
end
=20
everywhere, to just do delegation.

or rather:

def foo(*args, **keys, &block)
bar(*args, **keys, &block)
end

What's wrong with having to do that? The old delegation would
be able to delegate to any of the old non-named argument method
definitions. Existing code would not break. Only when you
start using new named arguments in your method definitions
would you have to fix the delegation methods.

I think you need to choose whether you want separation between
named and positional arguments (like lisp and command-line
options/args) or not (like python/sydney). And go with it.=20
Having named arguments appear in the positional *args does not
show good separation - what you want I think.
|Also, like we have array splatting when you call a method, I
|think hash splatting would also be useful. matz said that
|hashes would be automatically splatted if they were the last
|argument, but this results in ambiguity:
|
|def foo(a,b=3D{},c:2,**keys) ... end
|
|foo("a",{:x =3D> 1,:y =3D> 2})
|
|Does the above result in:
|
|a =3D "a", b =3D {:x =3D> 1,:y =3D> 2}, c =3D 2, keys =3D {}
|
|or:
|
|a =3D "a", b =3D {}, c =3D 2, keys =3D {:x =3D> 1,:y =3D> 2}
=20
The former. Keyword argument uses the last remaining hash.

This contradicts what you said above about method delegation.=20
If we had this delegation with the above foo:

def bar(*args)
foo(*args)
end

bar("a", x: 1, y: 2)
# bar: args =3D ["a",{:x =3D> 1, :y =3D> 2}]
# foo: a =3D "a", b =3D {:x =3D> 1,:y =3D> 2}, c =3D 2, keys =3D {}

That's not what you wanted for delegating. It should be:

foo: a =3D "a", b =3D {}, c =3D 2, keys =3D {:x =3D> 1,:y =3D> 2}

But then if you choose that, you have no way of passing a hash
to the last positional argument and giving no named arguments
(assuming they all have defaults).

I think with named arguments you should not give special
meaning to Hash or you'll run into the same problems you had
with multiple-value returns and assignments that you had with
Array. Explicitly splatting/unsplatting a Hash for named
arguments makes sense, but I don't think you should tie Hash as
part of the definition of named arguments.
|I'm hoping that we have enough reflection for these named
|arguments like #arity. I'm thinking that with named
arguments=20
|a potential simple application would be as a spec for
handling
|command-line options (named arguments). I'd like to see all
of
|the keywords and defaults accessible from Proc/Method
objects.=20
|It'd be nice to have access to defaults of positional args
|while wer'e asking.
=20
Accepting keywords should be obtained from some kind of
reflection
API, although the API is not fixed yet. But I'm not sure
default
values can be accessed since they are arbitrary expression,
and we
have no good way to represent pre-evaluated expression in
Ruby (unlike
in Lisp, where we can use S expression).

Sorry, I forgot that defaults can be expressions. I guess you
could have something that just says whether an arg (positional
or named) has a default or not. Another option is to be able
to access the default as a Proc. To evaluate the default,
you'd just #call it like any other Proc.
|Since Ruby 2 will also be completely redoing how
|multiple-values are handled, why not also consider being
able
|to name your return values? If a method returns a bunch of
|attributes, it would be nice for the caller to be able to
pick
|what they want.
=20
I have no good idea of multiple value binding for named
values
(i.e. syntax). Do you?
=20
matz.

Actually, I do. If you put the ability to handle named
arguments in a multi-assign, you'll have it. The LHS would
look like argument definitions in a def (except defaults aren't
allowed) and the RHS (or return) would look like a method call.
The problem is that this will force the named argument to be
put in a variable of the same name when assigning to the LHS.=20
To fix this problem, I propose that you be allowed to specify a
different local variable name for a named argument in a method
definition or LHS:

def foo(
a, # position: 0, local var: a, required
b=3D"b", # position: 1, local var: b, default: "b"
*args, # position: 2..-1, local var: args=20
w:, # name: w, local var: w, required
x:c, # name: x, local var: c, required
y:=3D"y", # name: y, local var: y, default: "y"
z:d=3D"z",# name: z, local var: d, default: "z"
**keys) # name: others, local var: keys
....
# maybe w: could be made equivalent to w:w
return a,b,*args,w:w,x:c,y:y,z:d,**keys
end

# only care about pos:0 and name:x - throw out the rest
aa,x:xx =3D foo(0,1,2,w:3,x:4,m:5)

Like you allow discarded positional args ("_"), you could also
allow discarded named args (name:_[=3Ddefault]). And "**" like
"*" could mean accept any other keys but throw them out.

BTW, I'd still like to see the ability to specify required
positional args after the *args and/or default positional args.
I see no reason why any of this named argument stuff would
conflict with this (RCR 315).



=09
__________________________________=20
Yahoo! FareChase: Search multiple travel sites in one click.
http://farechase.yahoo.com
 
L

Louis J Scoras

------=_Part_30460_2815067.1130182199669
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

Right, so this is probably a really dumb idea, but I was just having a
little bit of fun. It would be cool if you could define methods like this:

define :foo do
required :bar, :baz
optional :request =3D> 'useless'
named :side,
:meat =3D> 'fish'
body do
puts "Good, I got to say #{foo}#{bar}."
puts "You thought of my third request was #{request}"
puts "For dinner we have #{meat}"
if side
puts "With a side of #{side}"
end
end
end

foo 'hello ', 'world', :meat =3D> 'steak', :side =3D> 'potatoes'

That is the first few times you wrote a definition like that. Then it would
get very old very quickly I'm assuming ;)

------=_Part_30460_2815067.1130182199669--
 
T

Trans

Widdle it:

def foo( required :bar, :baz ; optional :request=>'useless' ; named
:side, :meat=>'fish' )

def foo( bar, baz ; optional :request=>'useless' ; named :side,
:meat=>'fish' )

def foo( bar, baz, request='useless', keys: side, meat=>'fish' )

def foo( bar, baz, request='useless', side=>nil, meat=>'fish' )

T.
 
L

Louis J Scoras

------=_Part_433_5123122.1130187065139
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

Widdle it:


*agrees*

def foo( required :bar, :baz ; optional :request=3D>'useless' ; named
:side, :meat=3D>'fish' )

def foo( bar, baz ; optional :request=3D>'useless' ; named :side,
:meat=3D>'fish' )

That one is very much like the lisp defun if I'm not mistaken.

def foo( bar, baz, request=3D'useless', keys: side, meat=3D>'fish' )
def foo( bar, baz, request=3D'useless', side=3D>nil, meat=3D>'fish' )


That looks reasonable. What's wrong with that? In the calling code it
wouldn't be ambiguous because there's: a fixed number of required
parameters; optional parameters come after the required ones, and any
keywords are specified with '=3D>' as they are currently.

Why are keyword parameters needed anyway? Is there a problem with the
implicit hash?
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top