A comparison by example of keyword argument styles

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

Hi,

That is, instead of:

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

Right syntax is

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

Something along the lines of:

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

where parameters to the left of the colon (or whatever symbol/keyword
you choose) are positional parameters and those to the right are named
ones.

If your model is CLISP, this is also
how CLISP deals with this (using &key), like:

   (defun foo ( &key a b c ) (list a b c))

I considered that idea too, but I chose similarity to calling
syntax.

              matz.

···

In message "Re: A comparison by example of keyword argument styles" on Sun, 23 Oct 2005 12:37:00 +0900, "gga" <ggarra@advancedsl.com.ar> writes:

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

that was my point :wink: 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"

:wink:

-a

···

On Mon, 24 Oct 2005, Trans wrote:
--

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

===============================================================================

Widdle it:

*agrees*

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

:side, :meat=>'fish' )

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

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

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

def foo( bar, baz, request='useless', side=>nil, 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.

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

···

On 10/24/05, Trans <transfire@gmail.com> wrote:

--
Lou

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'

···

On 10/24/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

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.

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.

Trans wrote:

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 :wink:

   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

Hi,

···

In message "Re: A comparison by example of keyword argument styles" on Tue, 25 Oct 2005 16:27:02 +0900, gabriele renzi <surrender_it@-remove-yahoo.it> writes:

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.

Hi,

···

In message "Re: A comparison by example of keyword argument styles" on Tue, 25 Oct 2005 22:37:45 +0900, Eric Mahurin <eric_mahurin@yahoo.com> writes:

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

No.

              matz.

i agree with you. in my parseargs module i have Argument and Keyword classes.
Keyword inherits from Argument. in this case *args is a list of Parameters so
you have access to things like

   arg.requred?
   arg.name
   arg.value

etc. objectification of paramerter types into a parameter list would be a
good start.

-a

···

On Wed, 26 Oct 2005, Tanaka Akira wrote:

In article <1130222755.861386.32126.nullmailer@x31.priv.netlab.jp>,
Yukihiro Matsumoto <matz@ruby-lang.org> writes:

>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).

I think an array, *args, is a structure too poor to
represent the all arguments.

How about **keys contains positional arguments and block?
A hash is enough rich to represent positional argument and
block addition to keyword arguments.

def foo(**keys)
p keys
bar(**keys)
end

foo(1, 2, k:3) {}

=> {:k => 3, "positional" => [1, 2], "block" => lambda {}}

This means keys contains special pairs for positional
arguments and block. In this example, "positional" and
"block" is used because strings are disjoint from symbols as
hash keys.

--

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

===============================================================================

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

-austin

···

On 11/9/05, Warren Seltzer <warrens@actcom.net.il> wrote:

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.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Hi,

At Sun, 23 Oct 2005 21:32:30 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:162119]:

>Something along the lines of:
>
> def function( a, b, c : d, e = 1, f )
> end
>
>where parameters to the left of the colon (or whatever symbol/keyword
>you choose) are positional parameters and those to the right are named
>ones.

>If your model is CLISP, this is also
>how CLISP deals with this (using &key), like:
>
> (defun foo ( &key a b c ) (list a b c))

I considered that idea too, but I chose similarity to calling
syntax.

What about semicolon like as block parameters.

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

···

--
Nobu Nakada

Yukihiro Matsumoto wrote:

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?

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

···

On Tue, 25 Oct 2005, Louis J Scoras wrote:

On 10/24/05, Trans <transfire@gmail.com> wrote:

Widdle it:

*agrees*

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

:side, :meat=>'fish' )

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

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

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

def foo( bar, baz, request='useless', side=>nil, 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.

--

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

===============================================================================

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"}]

these cannot be distinguished.

-a

···

On Tue, 25 Oct 2005, Louis J Scoras wrote:

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.

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'

--

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

===============================================================================

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
    ...

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

···

On Tue, 25 Oct 2005, Trans wrote:
--

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

===============================================================================

Hi,

···

In message "Re: A comparison by example of keyword argument styles" on Sun, 23 Oct 2005 23:15:23 +0900, nobu.nokada@softhome.net writes:

I considered that idea too, but I chose similarity to calling
syntax.

What about semicolon like as block parameters.

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

How do I call them? It doesn't seem look like calling convention.

              matz.

At first glance I like this, but ...

- with only keyword args, it looks a little ugly:
  def function(; d, e = 1, *f)

- in 1.9, ";" is already used as a delimiter (in the argument
list) for block local variables. I assume keyword args will be
allowed with blocks/lambdas.

Just for reference, another language that takes "keyword"
arguments is verilog (hardware language). It is ugly, but it
looks like this:

foo ( .a(1), .b(2), .c(3) ) # order doesn't matter
foo ( 1, 2, 3 ) # a=1, b=2, c=3

···

--- nobu.nokada@softhome.net wrote:

Hi,

At Sun, 23 Oct 2005 21:32:30 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:162119]:
> >Something along the lines of:
> >
> > def function( a, b, c : d, e = 1, f )
> > end
> >
> >where parameters to the left of the colon (or whatever
symbol/keyword
> >you choose) are positional parameters and those to the
right are named
> >ones.
>
> >If your model is CLISP, this is also
> >how CLISP deals with this (using &key), like:
> >
> > (defun foo ( &key a b c ) (list a b c))
>
> I considered that idea too, but I chose similarity to
calling
> syntax.

What about semicolon like as block parameters.

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

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005

Nabu wrote:

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.

Ara.T.Howard wrote:

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.