A comparison by example of keyword argument styles

A

Ara.T.Howard

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 => 'useless'
named :side,
:meat => '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 => 'steak', :side => 'potatoes'

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


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

def meth *argv
pa =
parseargs(argv) do
req_arg 'foo', 'bar'
opt_arg 'request' => 'useless'
opt_kw 'side'
opt_kw 'meat' => 'fish'
end

puts <<-txt
Good, I got to say #{ pa.foo } #{ pa.bar }.
You thought of my third request was #{ pa.request }
For dinner we have #{ pa.meat }
txt

if pa.side
puts "With a side of #{ pa.side }"
end
end

meth 'hello ', 'world', 'useless', :meat => 'steak', :side => 'potatoes'

harp:~ > ruby a.rb
Good, I got to say hello world.
You thought of my third request was useless
For dinner we have steak
With a side of potatoes


note. it's impossible to determine, if an argument is optional (your 'request'
above) if the last hash passed __is__ that optional argument itself or if it's
the set of keywords. therfore you must pass 'useless' for the request in this
case - if you think about it the parsing is otheriwse impossible.

cheers.

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

Ara.T.Howard

*agrees*

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

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

def foo( bar, baz, request='useless', keys: side, meat=>'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 '=>' as they are currently.

not really, think about it

request = 'side' => 'beans', 'meat' => 'tofu'

foo 'bar', 'baz', request

have you passed request as 'request' or the keywords? how bout here:

foo 'bar', 'baz', 'side' => 'beans', 'meat' => 'tofu'

and here

foo 'bar', 'baz', nil, 'side' => 'beans', 'meat' => 'tofu'

is request 'nil' and therfore is not assigned the default value? or is nil
clobbered with the default? if it's clobbered how can you ever call a method
that accepts an optional argument with default with a value of nil and not
have it clobbered? eg. how could you do this:

def foo optional = 42
end

how do you call it with optional == nil?

if this works

foo nil

then this cannot work

def foo required, optional = 42, keyword => 'forty-two'
end

since you cannot call foo with both a keyword AND get the default value for
optional. this won't work

foo 42, 'keyword' => 40 + 2

since you are simply passing a hash as the value for optional. and keyword is
not set - and this won't work

foo 42, nil, 'keyword' => 40 + 2

since now optional is nil and not the default value of 42.

i think you cannot ever have both trailing optional arguments AND keywords
with current ruby calling conventions since it will always be impossible to
determine whether the last hash is a value for an optional argument or the set
of keywords. the only way is to have a syntax that calls methods with
keywords that are not an inlines hash, eg

foo 42, keyword : 42.0 # optional gets default value
foo 42, keyword => 42.0 # optional IS a hash , keyword is not given

when i was coding parseargs all this became apparent very quickly. trying to
write a parser for any proposed syntax is a good exercise that shows how nice
a real keyword syntax might make things.

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

Louis J Scoras

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

meth 'hello ', 'world', 'useless', :meat =3D> 'steak', :side =3D> 'potato= es'

harp:~ > ruby a.rb
Good, I got to say hello world.
You thought of my third request was useless
For dinner we have steak
With a side of potatoes


note. it's impossible to determine, if an argument is optional (your
'request'
above) if the last hash passed __is__ that optional argument itself or if
it's
the set of keywords. therfore you must pass 'useless' for the request in
this
case - if you think about it the parsing is otheriwse impossible.
Couldn't you require an explicit hash then, if it is intended to be slurped
into the optional parameter? You need to magically realize that this is a
hash in the first place.

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

response =3D 'useless'
meat =3D 'steak'
side =3D 'potatoes'

Would be distinguished from this:

meth 'hello ', 'world', {:meat =3D> 'steak', :side =3D> 'potatoes'}

response =3D {:meat =3D> 'steak', :side =3D> 'potatoes'}

And this:

meth 'hello ', 'world', {:meat =3D> 'steak'}, :side =3D> 'potatoes'

response =3D {:meat =3D> 'steak'},
side =3D 'potatoes'

------=_Part_913_19583841.1130188609718--
 
A

Ara.T.Howard

Couldn't you require an explicit hash then, if it is intended to be slurped
into the optional parameter? You need to magically realize that this is a
hash in the first place.

meth 'hello ', 'world', :meat => 'steak', :side => 'potatoes'

response = 'useless'
meat = 'steak'
side = 'potatoes'

Would be distinguished from this:

meth 'hello ', 'world', {:meat => 'steak', :side => 'potatoes'}

response = {:meat => 'steak', :side => 'potatoes'}

And this:

meth 'hello ', 'world', {:meat => 'steak'}, :side => 'potatoes'

response = {:meat => 'steak'},
side = 'potatoes'

harp:~ > cat a.rb
def meth *a, &b
p a
end

meth 'hello', 'world', :meat => 'steak', :side => 'potatoes'
meth 'hello', 'world', {:meat => 'steak', :side => 'potatoes'}


harp:~ > ruby a.rb
["hello", "world", {:meat=>"steak", :side=>"potatoes"}]
["hello", "world", {:meat=>"steak", :side=>"potatoes"}]


these cannot be distinguished.


-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:
i think you cannot ever have both trailing optional arguments AND keywords
with current ruby calling conventions since it will always be impossible to
determine whether the last hash is a value for an optional argument or the set
of keywords. the only way is to have a syntax that calls methods with
keywords that are not an inlines hash, eg

foo 42, keyword : 42.0 # optional gets default value
foo 42, keyword => 42.0 # optional IS a hash , keyword is not given

Ah, at first I was gogin to say "STOP! You've got this all distorted.
These are parameters, not hash keys. Related but different. So why are
you using strings? It's this:

foo 42, keyword => 40 + 2

There is no ambiguity."

But now I see you get to this To which I say: Exactly. This is how it
ought to be -- IF done this way. Matz implementation actually is NOT
like this though and has worse problems some of which you describe
above.
when i was coding parseargs all this became apparent very quickly. trying to
write a parser for any proposed syntax is a good exercise that shows how nice
a real keyword syntax might make things.

What do you mean by real?

T.
 
A

Ara.T.Howard

Ah, at first I was gogin to say "STOP! You've got this all distorted. These
are parameters, not hash keys. Related but different. So why are you using
strings? It's this:

foo 42, keyword => 40 + 2

There is no ambiguity."

But now I see you get to this To which I say: Exactly. This is how it ought
to be -- IF done this way. Matz implementation actually is NOT like this
though and has worse problems some of which you describe above.

it's slippery all right.
What do you mean by real?

i mean something other that collected trailing hash keys.

mind you, i'm happy with the current situation. but if syntax support would
be added to ruby it's a slippery slope when you through optional arguments,
and trailing open hashes into the mix.

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

Trans

def meth *argv
pa =
parseargs(argv) do
req_arg 'foo', 'bar'
opt_arg 'request' => 'useless'
opt_kw 'side'
opt_kw 'meat' => 'fish'
end

I idea is good but may I suggest it be a little less "cryptic"?
Something more like:

def meth *parm
parm = Parameters.new(parm) do
args :foo, :bar, :request => 'useless'
keys :side => nil, :meat => 'fish'
end
parm.foo
...

You could also to set parm automatically, and of course a #parameters
constructor method is fine if you prefer, producing:

def meth *parm
parameters(parm) do
args :foo, :bar, :request => 'useless'
keys :side => nil, :meat => 'fish'
end
parm.foo
...

T.
 
A

Ara.T.Howard

I idea is good but may I suggest it be a little less "cryptic"?
Something more like:

def meth *parm
parm = Parameters.new(parm) do
args :foo, :bar, :request => 'useless'
keys :side => nil, :meat => 'fish'
end
parm.foo
...

how do you say that an argument is optional then? are all keywords implied as
optional? why should it be so? parseargs handles both cases. by allowing

required_arguments
optional_arguments

required_keywords
optional_keywords

and shortcuts such as

req_arg, ra
req_kw, kw

of course you can say

arguments
keywords

all of the above (and there are more) takes lists. and these are by default
required. eg

def meth *argv
pa = parseargs argv do
arguments %w( foo bar )
keywords %w( foobar barfoo )
end
...
end

the reason i do not allow

arguments 'foo', 'bar' => 42

but do allow

argument 'foo' => 42

is that the options can be used to convey much more that a default value in
parseargs. it can be used to specify type, ducktype, coersion, default procs
instead of value only, etc. for example

arguments 'foo', 'bar', 'default' => 42, 'type' => Fixunm, 'ducktype' => 'to_i'

argument 'n', 'types' => [Fixunm, Float]

argument 'n', 'coerce' => :to_i

argument 'n', 'coerce' => lambda{|n| Integer n}

argument 's', 'ducktype' => %w( upcase downcase gsub )

so, basically, if you pass a __single__ argument that's a hash as in

argument 'foo' => 42

i know 42 is the default value for foo. if you pass more than one argument as
in

argument 'foo', 'bar', 'default' => '42', 'corece' => 'to_s'

i can't tell if the last hash is full of paramter names and default values or
metadata like type info and coercsion info. sure - it's obvious to the eye,
but makeing any assumptions would disallow this

argument 'default' => 42

here default is a parameter with a default value of 42. sticky.

you can read all about it here

http://codeforpeople.com/lib/ruby/parseargs/parseargs-0.3.0/README
You could also to set parm automatically, and of course a #parameters
constructor method is fine if you prefer, producing:

def meth *parm
parameters(parm) do
args :foo, :bar, :request => 'useless'
keys :side => nil, :meat => 'fish'
end
parm.foo

but that cannot be done? you cannot munge 'parm' in place and set it to
another variable? perhaps i'm not understanding?

cheers.

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

Yukihiro Matsumoto

Hi,

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

|> otherwise we
|> must change the code as
|>
|> def foo(*args, **keys)
|> bar(*args, **keys)
|> end
|>
|> 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?

It's longer than it is really needed. I want to delegate everything
I've passed, that's all. I'd rather delegate blocks as well when I
write bar(*args).

matz.
 
G

gabriele renzi

Yukihiro Matsumoto ha scritto:
Hi,

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

|> otherwise we
|> must change the code as
|>
|> def foo(*args, **keys)
|> bar(*args, **keys)
|> end
|>
|> 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?

It's longer than it is really needed. I want to delegate everything
I've passed, that's all. I'd rather delegate blocks as well when I
write bar(*args).

matz.

sorry if this was already explained, how do I do if I don't want to
delegate everything?
 
A

Ara.T.Howard

Hi,

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

|> otherwise we
|> must change the code as
|>
|> def foo(*args, **keys)
|> bar(*args, **keys)
|> end
|>
|> 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?

It's longer than it is really needed. I want to delegate everything
I've passed, that's all. I'd rather delegate blocks as well when I
write bar(*args).

amen! maybe bar(_).

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

Daniel Schierbeck

Trans said:
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.

AAARGH! My eyes ;)

def foo(a, b = "b", named c, named d = "d", **keys); end

foo("a", :c => "c", :e => "e")
-> a = "a", b = "b", c = "c", d = "d"
-> keys = {:e => "e"}

Of course you don't need to use the word "named", you could substitute
it with something else.


Cheers,
Daniel
 
Y

Yukihiro Matsumoto

Hi,

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

|> It's longer than it is really needed. I want to delegate everything
|> I've passed, that's all. I'd rather delegate blocks as well when I
|> write bar(*args).

|sorry if this was already explained, how do I do if I don't want to
|delegate everything?

Good point. Both should be possible.

matz.
 
T

Trans

Ara.T.Howard said:
how do you say that an argument is optional then? are all keywords implied as
optional? why should it be so? parseargs handles both cases. by allowing

It's made optional when a default value is specified (via '=>
default'). Certainly if it has a default it _must_ be optional.
required_arguments
optional_arguments

required_keywords
optional_keywords

[snip]

arguments 'foo', 'bar', 'default' => 42, 'type' => Fixunm, 'ducktype' => 'to_i'

argument 'n', 'types' => [Fixunm, Float]

argument 'n', 'coerce' => :to_i

argument 'n', 'coerce' => lambda{|n| Integer n}

argument 's', 'ducktype' => %w( upcase downcase gsub )

so, basically, if you pass a __single__ argument that's a hash as in

argument 'foo' => 42

i know 42 is the default value for foo. if you pass more than one argument as
in

argument 'foo', 'bar', 'default' => '42', 'corece' => 'to_s'

I see. You're doing a great deal more here then I realized.

It is interesting that this is very much like some systems I
seen/worked on for class attributes as well. Have you considered the
applicability there?
From my expereince with those I have come to like the idea of a
separate desgination for such. For example in this case I might do:

arguments 'foo' => 42, 'bar' => 42
argschema 'foo', 'bar', 'type' => Fixnum, 'ducktype' => 'to_i'

Nonetheless I wonder if this isn't all simply going to far. Is it that
much less convenitent to do:

foo = (foo || 42).to_i

or

foo = foo || 42
raise ArgumentError unless foo.is_a?(Fixnum)

BTW the to_i or the Fixnum is superflous. Only one or the other is
useful.
i can't tell if the last hash is full of paramter names and default values or
metadata like type info and coercsion info. sure - it's obvious to the eye,
but makeing any assumptions would disallow this

argument 'default' => 42

here default is a parameter with a default value of 42. sticky.

you can read all about it here

http://codeforpeople.com/lib/ruby/parseargs/parseargs-0.3.0/README

Thanks. I'll have a look.
but that cannot be done? you cannot munge 'parm' in place and set it to
another variable? perhaps i'm not understanding?

You could use singleton methods to transform the array into a new
duck-class. Or use the binding to reset the var (albiet that might have
some limitations regarding the name of parm). But this is a minor aside
anyway.

T.
 
E

Eric Mahurin

--- Yukihiro Matsumoto said:
Hi,
=20
In message "Re: A comparison by example of keyword argument
styles"
on Tue, 25 Oct 2005 00:15:35 +0900, Eric Mahurin
=20
|> 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?
=20
It's longer than it is really needed. I want to delegate
everything
I've passed, that's all. I'd rather delegate blocks as well
when I
write bar(*args).
=20
matz.

So would a Proc for the block appear as the last element of
args? If so, you'd have ambiguity between a Proc as the last
positional arg (with no block) and having a block. Or would
you have a special class for blocks? Either way you'd create
compatibility problems. Or maybe args wouldn't be a normal
Array and "hide" the block (and named args)? Splatting this
would reveal the block and/or named args when you delegate.

What if you want to pass on the positional args but give your
own block (or named arguments)?

If delegation is the only reason for wanting named-args (and
blocks) to appear in the positional args, what about having a
new syntax that splats/unsplats all types of arguments
(positional, named, block) into a special object? This object
would closely correlate to the method-call mechanism so that
minimal overhead is needed. Maybe *&:

def foo(*&args) # grab positional, named, and block
bar(*&args) # pass positional, named, and block
end

The *& would have to be the last "argument" (positional, named,
block) given in the method definition and method call. Maybe
you would allow positional arguments to come before it - but no
other kind.

Matz, what did you think about the idea of being able to use
named arguments in a multi-assign so that you could return
named arguments? Like I said, I think the only thing that is
needed to make this reasonable is the ability to have the
keyword and the local variable name different. Then make make
the multi-LHS look similar to arguments in a method definition
and a multi-RHS/return look like arguments in a method call.



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

Yukihiro Matsumoto

Hi,

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

|So would a Proc for the block appear as the last element of
|args?

No.

matz.
 
W

Warren Seltzer

Consider respond_to?

Right now we match on method name. In the future we may well want to match on the full
signature. Or we might want to match on method and use reflection to discover the
signature.

A keyword scheme should be able to support this.

Warren Seltzer
 
A

Austin Ziegler

Consider respond_to?

Right now we match on method name. In the future we may well want to mat= ch on
the full signature. Or we might want to match on method and use reflecti= on to
discover the signature.

I don't think that'll be the case, honestly.

-austin
 

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,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top