A comparison by example of keyword argument styles

Hello fellow rubyists,

What I have bellow is what started as a post to RedHanded. It was
growing in size too rapidly so I decided to post here for all to see.
Sorry for starting yet another thread on these topics. It is rough so
please don't nit pick details. I don't want this to start a flame war
(though I can't do much about that now). I would rather see some ideas
on how to get the best of both worlds. Some of this won't come without
a compromise so keep that in mind. I apologize in advance if I did
make any grievous errors in my interpretations.

There is a matter of taste involved but beyond that there are a few
easy comparisons. I will try to keep this down to just that (though a
few may be on a grey line, I hope they are clear enough).

Let me cite Matz's slides first:
* Make method calls more descriptive
* Order free arguments

With that simple goal in mind, lets start the comparisons.

Sydney's argument scheme (s_ henceforth) is simple when you want to
reorder the arguments.

def s_f1(a,b,c) ... end
s_f1(b:2, a:1, c:3)

Matz's scheme (m_ from now on) allows this too:

def m_f1(a:,b:,c:) ... end
m_f1(b:2, a:1, c:3)

Ok. Not much difference right away no clear winner. Lets examine
another variation on calling these:

s_f1(1,2,3) # Simple. current behavior.
m_f1(1,2,3) # Error. No positional arguments.

This shows one point that goes to s_. It makes it easy to use keyword
args that still have position. However, Matz could consider to allow
keyword arguments to count as positional arguments and make this
example go away. It is up to him. +1 for s_ for now. The change would
force non keyword args to come before keyword args. simple enough.
Though I still don't see a good reason to share both positional and
keyword arguments (a good example would be of great help in this
discussion).

The next example will be a method that takes any number of key-worded arguments:

def m_f2(**keys) ... end
m_f2(k1: 1, k2: 2, k3: 3)

def s_f2(*args) ... end
s_f2(k1: 1, k2: 2, k3: 2)

That works but there are some complications that the s_ method starts
to see (the hidden ugly head). *args now gets an array with hash
(key-value) or a value with each entry. Ugly. Now something internal
is depending on how someone on the outside (away from the interface)
called it to see what it gets. I hope this is clear enough for you. +1
for m_.

How about mixing positional and keyword args?

def m_f3(p1, p2, k1:, k2:) ... end
def s_f3(p1, p2, k1, k2) ... end

*_f3(1,2,k1:3, k2: 4)

Not much difference. m_ requires the extra : to be added. This is
neither a plus or a minus as it can be easily argued both ways. No
winner. (I will argue it if needed but trust me one can look both
ways).

How about having a variable number of positional arguments and a set
number of keys?

def m_f4(*args, a:, b:)
m_f4(1,2,3,4,5,6,7,8, a:1, b:2) # misleading see bellow.

def s_f4(a, b, *args)
s_f4(1,2,3,4,5,6,7,8, a:1, b:2) # might have the same problem

The s_ example is nice. It show an intuitive behavior at first but
depending on implementation you can no longer pull a variable number
of key paris or you have the same semantic problem that the m_ one
has. If you use * at all that allows any number of arguments of any
type to be passed. Assuming the latter behavior (needed for *args to
work with delegation), then neither has any gain. I may be miss
understanding s_ at this point so please point it out.

How about having both keyword and positional arguments mixed in a
catch-all with *?

def m_f5(*args)
m_f5(1,2, a:3, b:4)

def s_f5(*args)
s_f5(1,2 a:3, b:4)

Well things start to contrast now. For s_ you get: [1,2, { :a => 3}, {
:b => 4}] if I understand correctly. m_ gives you [1,2, {:a => 3, :b
=> 4}]. I won't debate on which is better in this case. Most of this
is involved with opinion. However, if you want to look at positionals
alone and keys alone it is easy with m_ we now have the hash collected
at the end of *args and can use **keys if we want to. Not a huge plus
but a point to make things easy. It will minimize boilerplate code on
a method. I give m_ a +1, you may disregard it if you don't agree.

Now think about the above before we move on. Keep in mind that it is
not just another way to call a method but gives the method's interface
a richer meaning (like Matz's slide said).

Now for some more concrete examples of usage:

Say we have an object that we create from a class through some special
method. The some arguments are required while others may or may not be
there but the circumstances differ. Imagine that the list of
attributes that can be passed may become quite long so using default
arguments wouldn't be a very good idea. Or even further, the keys
might be passed to a second function. This would normally be odd code
to see but it shows how the nature of the two methods differ by quite
a bit in real use.

# Untested code. Could contain errors. At least I have an excuse this time.
class Pet
  def self.m1_create(kind, name, **keys)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    case(kind)
    when :ham
      pet.weight = keys[:weight]
    when :cat
      pet.color = keys[:color]
    when :dog
      pet.color = keys[:color]
      pet.breed = keys[:breed]
    when :ruby
      pet.facets = keys[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end

  # Same as m1_ but with a different method argument style.
  def self.m2_create(kind:, name:, **keys)
    # Lazy me :wink: They are the same otherwise anyway.
    m1_create(kind,name,**keys)
  end

  def self.s_create(kind, name, *args)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    # Messy solution. There is probably a better one.
    get = lambda {|sym|
      args.find(lambda{{}}) {|e|
        e.kind_of? Hash && e[sym]
      }[sym]
    }
    case(kind)
    when :ham
      pet.weight = get[:weight]
    when :cat
      pet.color = get[:color]
    when :dog
      pet.color = get[:color]
      pet.breed = get[:breed]
    when :ruby
      pet.facets = get[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end
end

Pet.m1_create(:ham, "selfish_ham", weight:2.3)
Pet.m2_create(kind: :cat, name: "cat43", color: :black)
Pet.s_create(:dog, "singleton", color: :brown, breed: :mini_pincher)
Pet.s_create(kind: :ruby, name: "JRuby", facets: 26)

My s_ method is messy and could probably be cleaned up but it still
serves a point. Savor the style for a bit. It might add more verbosity
but I think it gives us some good side effects for the small price
(IMHO again). I think some really good points can be made for both
side but my _feeling_ is that Ruby doesn't need another halfway there
feature (IMHO). Keyword arguments are serious things and should be
treated as part of your interface (IMHO). I feel that the semantics of
m_ are more clear than the at first simpler look of s_ (IMHO -- why
not just automatically append these till the end of my message). It is
a hard choice. We still have one more option that I know of, change
nothing. Hashes seem to get the job done for most people already. I
know I missed something so please add to this. If I made any errors
please correct them. Just avoid and unproductive and personal attacks
please.

Thanks for reading this far,
Brian.

Brian Mitchell wrote:

Hello fellow rubyists,

What I have bellow is what started as a post to RedHanded. It was
growing in size too rapidly so I decided to post here for all to see.
Sorry for starting yet another thread on these topics. It is rough so
please don't nit pick details. I don't want this to start a flame war
(though I can't do much about that now). I would rather see some ideas
on how to get the best of both worlds. Some of this won't come without
a compromise so keep that in mind. I apologize in advance if I did
make any grievous errors in my interpretations.

There is a matter of taste involved but beyond that there are a few
easy comparisons. I will try to keep this down to just that (though a
few may be on a grey line, I hope they are clear enough).

Let me cite Matz's slides first:
* Make method calls more descriptive
* Order free arguments

With that simple goal in mind, lets start the comparisons.

Sydney's argument scheme (s_ henceforth) is simple when you want to
reorder the arguments.

def s_f1(a,b,c) ... end
s_f1(b:2, a:1, c:3)

Matz's scheme (m_ from now on) allows this too:

def m_f1(a:,b:,c:) ... end
m_f1(b:2, a:1, c:3)

Ok. Not much difference right away no clear winner. Lets examine
another variation on calling these:

s_f1(1,2,3) # Simple. current behavior.
m_f1(1,2,3) # Error. No positional arguments.

This shows one point that goes to s_. It makes it easy to use keyword
args that still have position. However, Matz could consider to allow
keyword arguments to count as positional arguments and make this
example go away. It is up to him. +1 for s_ for now. The change would
force non keyword args to come before keyword args. simple enough.
Though I still don't see a good reason to share both positional and
keyword arguments (a good example would be of great help in this
discussion).

The next example will be a method that takes any number of key-worded
arguments:

def m_f2(**keys) ... end
m_f2(k1: 1, k2: 2, k3: 3)

def s_f2(*args) ... end
s_f2(k1: 1, k2: 2, k3: 2)

That works but there are some complications that the s_ method starts
to see (the hidden ugly head). *args now gets an array with hash
(key-value) or a value with each entry. Ugly. Now something internal
is depending on how someone on the outside (away from the interface)
called it to see what it gets. I hope this is clear enough for you. +1
for m_.

How about mixing positional and keyword args?

def m_f3(p1, p2, k1:, k2:) ... end
def s_f3(p1, p2, k1, k2) ... end

*_f3(1,2,k1:3, k2: 4)

Not much difference. m_ requires the extra : to be added. This is
neither a plus or a minus as it can be easily argued both ways. No
winner. (I will argue it if needed but trust me one can look both
ways).

How about having a variable number of positional arguments and a set
number of keys?

def m_f4(*args, a:, b:)
m_f4(1,2,3,4,5,6,7,8, a:1, b:2) # misleading see bellow.

def s_f4(a, b, *args)
s_f4(1,2,3,4,5,6,7,8, a:1, b:2) # might have the same problem

The s_ example is nice. It show an intuitive behavior at first but
depending on implementation you can no longer pull a variable number
of key paris or you have the same semantic problem that the m_ one
has. If you use * at all that allows any number of arguments of any
type to be passed. Assuming the latter behavior (needed for *args to
work with delegation), then neither has any gain. I may be miss
understanding s_ at this point so please point it out.

How about having both keyword and positional arguments mixed in a
catch-all with *?

def m_f5(*args)
m_f5(1,2, a:3, b:4)

def s_f5(*args)
s_f5(1,2 a:3, b:4)

Well things start to contrast now. For s_ you get: [1,2, { :a => 3}, {

b => 4}] if I understand correctly. m_ gives you [1,2, {:a => 3, :b

=> 4}]. I won't debate on which is better in this case. Most of this
is involved with opinion. However, if you want to look at positionals
alone and keys alone it is easy with m_ we now have the hash collected
at the end of *args and can use **keys if we want to. Not a huge plus
but a point to make things easy. It will minimize boilerplate code on
a method. I give m_ a +1, you may disregard it if you don't agree.

Now think about the above before we move on. Keep in mind that it is
not just another way to call a method but gives the method's interface
a richer meaning (like Matz's slide said).

Now for some more concrete examples of usage:

Say we have an object that we create from a class through some special
method. The some arguments are required while others may or may not be
there but the circumstances differ. Imagine that the list of
attributes that can be passed may become quite long so using default
arguments wouldn't be a very good idea. Or even further, the keys
might be passed to a second function. This would normally be odd code
to see but it shows how the nature of the two methods differ by quite
a bit in real use.

# Untested code. Could contain errors. At least I have an excuse this
time. class Pet
  def self.m1_create(kind, name, **keys)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    case(kind)
    when :ham
      pet.weight = keys[:weight]
    when :cat
      pet.color = keys[:color]
    when :dog
      pet.color = keys[:color]
      pet.breed = keys[:breed]
    when :ruby
      pet.facets = keys[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end

  # Same as m1_ but with a different method argument style.
  def self.m2_create(kind:, name:, **keys)
    # Lazy me :wink: They are the same otherwise anyway.
    m1_create(kind,name,**keys)
  end

  def self.s_create(kind, name, *args)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    # Messy solution. There is probably a better one.
    get = lambda {|sym|
      args.find(lambda{{}}) {|e|
        e.kind_of? Hash && e[sym]
      }[sym]
    }
    case(kind)
    when :ham
      pet.weight = get[:weight]
    when :cat
      pet.color = get[:color]
    when :dog
      pet.color = get[:color]
      pet.breed = get[:breed]
    when :ruby
      pet.facets = get[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end
end

Pet.m1_create(:ham, "selfish_ham", weight:2.3)
Pet.m2_create(kind: :cat, name: "cat43", color: :black)
Pet.s_create(:dog, "singleton", color: :brown, breed: :mini_pincher)
Pet.s_create(kind: :ruby, name: "JRuby", facets: 26)

My s_ method is messy and could probably be cleaned up but it still
serves a point. Savor the style for a bit. It might add more verbosity
but I think it gives us some good side effects for the small price
(IMHO again). I think some really good points can be made for both
side but my _feeling_ is that Ruby doesn't need another halfway there
feature (IMHO). Keyword arguments are serious things and should be
treated as part of your interface (IMHO). I feel that the semantics of
m_ are more clear than the at first simpler look of s_ (IMHO -- why
not just automatically append these till the end of my message). It is
a hard choice. We still have one more option that I know of, change
nothing. Hashes seem to get the job done for most people already. I
know I missed something so please add to this. If I made any errors
please correct them. Just avoid and unproductive and personal attacks
please.

Thanks for reading this far,
Brian.

Wow! Quite a comprehensive discussion. Thanks for writing this far! :slight_smile:

I think I have to digest this a bit, here are some first reading thoughts:

- Example for mixing keyword and positional args: like you showed, pos
args for mandatory, keyword args for multiple optional values.

- I guess Sydney's argument scheme will cause serious performance
drawbacks if there are multiple keyword arguments because there is one
hash per pair.

- Mixing both styles for a single method feels not right in Ruby because
it seems more complicated than necessary (just look at the length of the
part of your discussion that deals with this). Stored procedures of RDBMS
can usually be called with parameters given either positional style *or*
keyword style (btw, without a difference in declaration) but not mixed. I
would only allow one of the two approaches.

- Personally I'd probably favour an approach which allows only one
calling style at a time. Variables that have no default values declared
will just be nil. Basically this would be the equivalent of this with
nicer syntax:

old:
def foo(h)
  h[:length] * h[:width]
end

foo(:length => 10, :width => 20)

new:
def foo(length, width)
  length * width
end
foo 10, 20
foo width:20, length:10

Disclaimer: these remarks might not be too well thought out.

Kind regards

    robert

Selon Brian Mitchell <binary42@gmail.com>:

My s_ method is messy and could probably be cleaned up but it still
serves a point. Savor the style for a bit. It might add more verbosity
but I think it gives us some good side effects for the small price
(IMHO again). I think some really good points can be made for both
side but my _feeling_ is that Ruby doesn't need another halfway there
feature (IMHO). Keyword arguments are serious things and should be
treated as part of your interface (IMHO). I feel that the semantics of
m_ are more clear than the at first simpler look of s_ (IMHO -- why
not just automatically append these till the end of my message). It is
a hard choice. We still have one more option that I know of, change
nothing. Hashes seem to get the job done for most people already. I
know I missed something so please add to this. If I made any errors
please correct them. Just avoid and unproductive and personal attacks
please.

Thanks for the post Brian. I want to say that I agree with your conclusion that
matz's proposal is right now the best one. As I said on RedHanded, it's indeed
a question of interface. Keyword arguments are, when looking at them from an
interface point of view, like Smalltalk or Objective-C's multiword methods:
keyword arguments are part of the *name* of the method (that's even stronger
than being part of its signature IMHO). Positional arguments are *not* part of
the name of the function. Their position is part of its signature, but that's
all. Mixing both styles, positional and keyword, is thus a bad idea IMHO. It's
mixing things that exist on different levels. Keyword arguments, being just a
part of the name of a method, must be explicitly marked as such by the
developer, and shouldn't be allowed to be left out by the user, just like you
can't leave out the name of a method.

Implicitly making all arguments keyword arguments is like implicitly making all
arguments names part of the name of the method: it is actually more restrictive
than having to explicitly mark the keyword arguments (then you can at least
choose which arguments are part of the name of the method and which not).
Adding to that the possibility to call keyword arguments in a positional way
and you get a recipe for disaster. If, as a developer, you want some arguments
to be keyword arguments, make them explicitly so: *mean* it. Having half-baked
semi-keyword-semi-positional arguments is like not being able to choose between
two desserts on the menu, after an already heavy dinner: choose one or the
other, but don't take both. Besides the higher check, there's quite a chance
you'd end up sick.

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

I still haven't given up on my own style :slight_smile:

   def foo(:a, :b, :c = 3, **keys); end

   # these should all be valid
   foo :a => 1, :b => 2 # current style
   foo a: 1, b: 2 # matz' proposition
   foo :a = 1, :b = 2 # my style :smiley:

1) *Method definition*
      I think named arguments should have the same
      syntax as positional ones, just with a colon
      in front of it.

        def foo(a, b, :c, :d = "bar")

2) *Method calling*
      I think matz' style is preferable in many cases,
      but i still think the :key = value syntax should
      be there.

        h = Human.new "John Doe", :height = 178, :weight = 65 # metric :wink:
        car.drive to: LasVegas, :speed = 125

Cheers,
Daniel

Why not a Parameters class. This would allow cool things like:

  a = [ :a, :b, :c ]

  def foo(a.to_parm) #as def foo(a,b,c)
    #...
  end

And it would be more useful in the def too (Although maybe this would
be a different class):

  def foo(**parm)
    p parm.arguments
    p parm.named_arguments
    p parm.block.call
  end

  foo(1,2,:a=>3){ "block" }

  => [1,2]
     {:a=>3}
     "block"

I'm not so keen on sticking with symbols in the parameters list for
named parameters. I think there's too much ':' run-on. Things like this
are aweful:

  foo set: :vital, sort: :reverse, mod: :odds do |a: :express|
    # ...
  end

Short of getting a new demarker for Symbol, I think it would be much
better to do without them in parameters if possible. This would work
fine I think:

  def foo( a, b, c, x=>nil, y=>nil, z=>nil )
    # ...
  end

The def can be a shortened too:

  def foo( a, b, c, x=>, y=>, z=> )
    # ...
  end

And call them in the same way:

  foo( 1, 2, 3, x=>8, y=>9, z=>10 )

Which is *almost* exactly what we do now with hash parameters.

T.

Brian Mitchell wrote:

Hello fellow rubyists,

<snip>

Sydney's argument scheme (s_ henceforth) is simple when you want to
reorder the arguments.

def s_f1(a,b,c) ... end
s_f1(b:2, a:1, c:3)

Matz's scheme (m_ from now on) allows this too:

def m_f1(a:,b:,c:) ... end
m_f1(b:2, a:1, c:3)

Ok. Not much difference right away no clear winner.

Ruby has always favored implicitness over explicitness. The fact that
I don't have to do anything explicitly in Sydney makes it the winner
IMHO.

Lets examine
another variation on calling these:

s_f1(1,2,3) # Simple. current behavior.
m_f1(1,2,3) # Error. No positional arguments.

This shows one point that goes to s_. It makes it easy to use keyword
args that still have position. However, Matz could consider to allow
keyword arguments to count as positional arguments and make this
example go away. It is up to him. +1 for s_ for now. The change would
force non keyword args to come before keyword args. simple enough.
Though I still don't see a good reason to share both positional and
keyword arguments (a good example would be of great help in this
discussion).

The next example will be a method that takes any number of key-worded arguments:

def m_f2(**keys) ... end
m_f2(k1: 1, k2: 2, k3: 3)

def s_f2(*args) ... end
s_f2(k1: 1, k2: 2, k3: 2)

That works but there are some complications that the s_ method starts
to see (the hidden ugly head). *args now gets an array with hash
(key-value) or a value with each entry. Ugly.

No, it's a single hash - [{:k1=>1, :k2=>2, :k3=>3}]. In Matz's
implementation, there is no '*args' afaik, only '**keys', which is
{:k1=>, :k2=>2, :k3=>3}.

Now something internal
is depending on how someone on the outside (away from the interface)
called it to see what it gets. I hope this is clear enough for you. +1
for m_.

I don't understand what you mean here.

How about mixing positional and keyword args?

def m_f3(p1, p2, k1:, k2:) ... end
def s_f3(p1, p2, k1, k2) ... end

*_f3(1,2,k1:3, k2: 4)

Not much difference. m_ requires the extra : to be added. This is
neither a plus or a minus as it can be easily argued both ways. No
winner. (I will argue it if needed but trust me one can look both
ways).

How about having a variable number of positional arguments and a set
number of keys?

def m_f4(*args, a:, b:)
m_f4(1,2,3,4,5,6,7,8, a:1, b:2) # misleading see bellow.

def s_f4(a, b, *args)
s_f4(1,2,3,4,5,6,7,8, a:1, b:2) # might have the same problem

The s_ example is nice. It show an intuitive behavior at first but
depending on implementation you can no longer pull a variable number
of key paris or you have the same semantic problem that the m_ one
has.

Huh? What makes you think the number of key pairs is limited?

If you use * at all that allows any number of arguments of any
type to be passed. Assuming the latter behavior (needed for *args to
work with delegation), then neither has any gain. I may be miss
understanding s_ at this point so please point it out.

How about having both keyword and positional arguments mixed in a
catch-all with *?

def m_f5(*args)
m_f5(1,2, a:3, b:4)

def s_f5(*args)
s_f5(1,2 a:3, b:4)

Well things start to contrast now. For s_ you get: [1,2, { :a => 3}, {
:b => 4}] if I understand correctly. m_ gives you [1,2, {:a => 3, :b
=> 4}].

No. They are identical.

I won't debate on which is better in this case. Most of this
is involved with opinion. However, if you want to look at positionals
alone and keys alone it is easy with m_ we now have the hash collected
at the end of *args and can use **keys if we want to. Not a huge plus
but a point to make things easy. It will minimize boilerplate code on
a method. I give m_ a +1, you may disregard it if you don't agree.

Now think about the above before we move on. Keep in mind that it is
not just another way to call a method but gives the method's interface
a richer meaning (like Matz's slide said).

The ability to inspect positionals alone or keywords alone *will* be
possible, via Behaviors.

Now for some more concrete examples of usage:

Say we have an object that we create from a class through some special
method. The some arguments are required while others may or may not be
there but the circumstances differ. Imagine that the list of
attributes that can be passed may become quite long so using default
arguments wouldn't be a very good idea. Or even further, the keys
might be passed to a second function. This would normally be odd code
to see but it shows how the nature of the two methods differ by quite
a bit in real use.

# Untested code. Could contain errors. At least I have an excuse this time.
class Pet
  def self.m1_create(kind, name, **keys)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    case(kind)
    when :ham
      pet.weight = keys[:weight]
    when :cat
      pet.color = keys[:color]
    when :dog
      pet.color = keys[:color]
      pet.breed = keys[:breed]
    when :ruby
      pet.facets = keys[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end

  # Same as m1_ but with a different method argument style.
  def self.m2_create(kind:, name:, **keys)
    # Lazy me :wink: They are the same otherwise anyway.
    m1_create(kind,name,**keys)
  end

  def self.s_create(kind, name, *args)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    # Messy solution. There is probably a better one.
    get = lambda {|sym|
      args.find(lambda{{}}) {|e|
        e.kind_of? Hash && e[sym]
      }[sym]
    }

<snip>

No, you would use Behaviors. This hasn't been entirely fleshed out
yet, but it will likely look like this.

   def self.s_create(kind, name, *args)
      ...
      keys = KeywordBehavior.keyword_arguments
      ...
   end

So, in that particular example, Sydney takes one more line. I think
there will also be KeywordBehavior.positional_arguments and
KeywordBehavior.arguments. The former would be positional arguments
only, while the latter would be all arguments.

Note that this provides equal or greater flexibility with regards to
delegation, not less, as Matz suggested in one RedHanded comment.

I've committed the test suite to the SVN repository if folks want to
get a clearer idea of how things work (so far). It's
"test_keyword_arguments.rb" under test/ruby.

Regards,

Dan

"Ah, to Hek with interfaces. They're overrated anyways!", says Typo the
Duck. "You can Duckument cant you?"

  def phooey
     unless %how
       "This thing quacks like a #{%1}."
     else
       "This thing #{%how} like a #{%1}."
     end
   end

   phooey( "duck" )
   => "This thing quacks like a duck."

   phooey( "duck", how=>"looks" )
   => "This thing looks like a duck."

:slight_smile:

T.

Hello again,

I am updating my original post with more information, namely some
corrections for s_ and some new discussion at the bottom. I am will
say again that the stuff bellow probably still contains errors but I
hope that it is at least more accurate than my original. I left all
the original text there so we could have a nice comparison of now and
then. It also serves as a good example of how something might work for
an implementation that differs in a way that is congruent to my
original post.

Hello fellow rubyists,

What I have bellow is what started as a post to RedHanded. It was
growing in size too rapidly so I decided to post here for all to see.
Sorry for starting yet another thread on these topics. It is rough so
please don't nit pick details. I don't want this to start a flame war
(though I can't do much about that now). I would rather see some ideas
on how to get the best of both worlds. Some of this won't come without
a compromise so keep that in mind. I apologize in advance if I did
make any grievous errors in my interpretations.

There is a matter of taste involved but beyond that there are a few
easy comparisons. I will try to keep this down to just that (though a
few may be on a grey line, I hope they are clear enough).

Let me cite Matz's slides first:
* Make method calls more descriptive
* Order free arguments

With that simple goal in mind, lets start the comparisons.

Sydney's argument scheme (s_ henceforth) is simple when you want to
reorder the arguments.

def s_f1(a,b,c) ... end
s_f1(b:2, a:1, c:3)

Matz's scheme (m_ from now on) allows this too:

def m_f1(a:,b:,c:) ... end
m_f1(b:2, a:1, c:3)

Ok. Not much difference right away no clear winner. Lets examine
another variation on calling these:

s_f1(1,2,3) # Simple. current behavior.
m_f1(1,2,3) # Error. No positional arguments.

This shows one point that goes to s_. It makes it easy to use keyword
args that still have position. However, Matz could consider to allow
keyword arguments to count as positional arguments and make this
example go away. It is up to him. +1 for s_ for now. The change would
force non keyword args to come before keyword args. simple enough.
Though I still don't see a good reason to share both positional and
keyword arguments (a good example would be of great help in this
discussion).

The next example will be a method that takes any number of key-worded arguments:

def m_f2(**keys) ... end
m_f2(k1: 1, k2: 2, k3: 3)

def s_f2(*args) ... end
s_f2(k1: 1, k2: 2, k3: 2)

That works but there are some complications that the s_ method starts
to see (the hidden ugly head). *args now gets an array with hash
(key-value) or a value with each entry. Ugly. Now something internal
is depending on how someone on the outside (away from the interface)
called it to see what it gets. I hope this is clear enough for you. +1
for m_.

This was one part confusing me about s_. When we get our list it could
either be:

[1,2,3,4] or [{:k1 => 1, :k2 => 2, :k3 => 3}]

The problem is still there but in variation, the arguments might or
might not be in the hash. This blurring the line between the method
and the caller, which are currently two very separated things.
Checking this for an interface that handles not so simple calls might
be a burden more than an assistance. I will keep my conclusion between
the two.

Another problem brought to my attention by Matz's post is delegation.
One can not use this to delegate unless you keep you method interface
the same, this could be problematic over long chains of argument
passing (i.e. super could have unintended meaning now)

To clarify m_'s behavior I will copy some examples from Matz's slides
with some annotations:

def baz(*rest, a:4, b:0, **keys)
  ...
end

baz() # rest=, a=4, b=0, keys={} <- notice that in args has an
implicit {} for **keys
baz(1) # rest=[1], a=4, b=0, keys={} <- explicit keys do not count as
positionals
baz(a:1) # rest=[{a:1}], a=1, b=0, keys={a:1} <- * will always contain
the full set of passed variables.
baz(a:1, b:2) # rest=[{a:1, b:2}], a=1, b=2, keys={a:1, b:2}
baz(1, 2, b:2) # rest=[1, 2, {b:2}], a=4, b=2, keys={b:2} <-
interesting. This result is for passing on correct values for
delegation.
baz(c:2) # rest=[{c:2}], a=4, b=0, keys={c:2} <- another one to think about.

How about mixing positional and keyword args?

def m_f3(p1, p2, k1:, k2:) ... end
def s_f3(p1, p2, k1, k2) ... end

*_f3(1,2,k1:3, k2: 4)

Not much difference. m_ requires the extra : to be added. This is
neither a plus or a minus as it can be easily argued both ways. No
winner. (I will argue it if needed but trust me one can look both
ways).

How about having a variable number of positional arguments and a set
number of keys?

def m_f4(*args, a:, b:)
m_f4(1,2,3,4,5,6,7,8, a:1, b:2) # misleading see bellow.

def s_f4(a, b, *args)
s_f4(1,2,3,4,5,6,7,8, a:1, b:2) # might have the same problem

The s_ example is nice. It show an intuitive behavior at first but
depending on implementation you can no longer pull a variable number
of key paris or you have the same semantic problem that the m_ one
has. If you use * at all that allows any number of arguments of any
type to be passed. Assuming the latter behavior (needed for *args to
work with delegation), then neither has any gain. I may be miss
understanding s_ at this point so please point it out.

I will extend this with another way to explain it. *args means you can
also pass any number of keys also. This goes for both. This means
there is no way of having only variable numbers of positional
arguments and static numbers of keyword arguments.

m_f4(1,2,3, a:4, b:5, c:6) <- example of what I mean.

How about having both keyword and positional arguments mixed in a
catch-all with *?

def m_f5(*args)
m_f5(1,2, a:3, b:4)

def s_f5(*args)
s_f5(1,2 a:3, b:4)

Well things start to contrast now. For s_ you get: [1,2, { :a => 3}, {
:b => 4}] if I understand correctly. m_ gives you [1,2, {:a => 3, :b
=> 4}]. I won't debate on which is better in this case. Most of this
is involved with opinion. However, if you want to look at positionals
alone and keys alone it is easy with m_ we now have the hash collected
at the end of *args and can use **keys if we want to. Not a huge plus
but a point to make things easy. It will minimize boilerplate code on
a method. I give m_ a +1, you may disregard it if you don't agree.

I was wrong on this one. Let me try again.

For s_ you get: [1,2, { :a => 3, :b => 4}] if I understand correctly.
m_ gives you [1,2, {:a => 3, :b => 4}]. They seem to yield the same
result but let me give a new the s_ example to show how they are
practically the same:

def s_f6(a, b, *args)

s_f6(0, 1, 3, b:2)

s_f6's *args would yield something like [3, {:b => 2}] in this case
(If I get Daniel's reply correctly). a = 0, b = 1

s_f6(1, 3, a:0, b:2)

This time *args would look like [1, 3, {:b => 2}]. a = 0, b = 1.

This is an example of where it doesn't break which is in contrast to
my original interpretation. It seems neither would win here after
further analysis. It is a matter of style for this example.

Now think about the above before we move on. Keep in mind that it is
not just another way to call a method but gives the method's interface
a richer meaning (like Matz's slide said).

Now for some more concrete examples of usage:

Say we have an object that we create from a class through some special
method. The some arguments are required while others may or may not be
there but the circumstances differ. Imagine that the list of
attributes that can be passed may become quite long so using default
arguments wouldn't be a very good idea. Or even further, the keys
might be passed to a second function. This would normally be odd code
to see but it shows how the nature of the two methods differ by quite
a bit in real use.

# Untested code. Could contain errors. At least I have an excuse this time.
class Pet
  def self.m1_create(kind, name, **keys)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name
    case(kind)
    when :ham
      pet.weight = keys[:weight]
    when :cat
      pet.color = keys[:color]
    when :dog
      pet.color = keys[:color]
      pet.breed = keys[:breed]
    when :ruby
      pet.facets = keys[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end

  # Same as m1_ but with a different method argument style.
  def self.m2_create(kind:, name:, **keys)
    # Lazy me :wink: They are the same otherwise anyway.
    m1_create(kind,name,**keys)
  end

This method is much cleaner now:

  def self.s_create(kind, name, *args)
    pet = Pet.allocate
    pet.kind = kind
    pet.name = name

    get = args.last

    case(kind)
    when :ham
      pet.weight = get[:weight]
    when :cat
      pet.color = get[:color]
    when :dog
      pet.color = get[:color]
      pet.breed = get[:breed]
    when :ruby
      pet.facets = get[:facets]
    else
      fail "Uknown kind of pet: #{kind}"
    end
  end
end

Pet.m1_create(:ham, "selfish_ham", weight:2.3)
Pet.m2_create(kind: :cat, name: "cat43", color: :black)
Pet.s_create(:dog, "singleton", color: :brown, breed: :mini_pincher)
Pet.s_create(kind: :ruby, name: "JRuby", facets: 26)

My s_ method is messy and could probably be cleaned up but it still
serves a point. Savor the style for a bit. It might add more verbosity
but I think it gives us some good side effects for the small price
(IMHO again). I think some really good points can be made for both
side but my _feeling_ is that Ruby doesn't need another halfway there
feature (IMHO). Keyword arguments are serious things and should be
treated as part of your interface (IMHO). I feel that the semantics of
m_ are more clear than the at first simpler look of s_ (IMHO -- why
not just automatically append these till the end of my message). It is
a hard choice. We still have one more option that I know of, change
nothing. Hashes seem to get the job done for most people already. I
know I missed something so please add to this. If I made any errors
please correct them. Just avoid and unproductive and personal attacks
please.

I've updated quite a bit of the code and commentary to reflect things.
The last comparison is mostly equal however, it does show some
ambiguities with calling in the future for both methods:

def foo(*args) ... end

foo(1, 2, a:3, b:4)
foo(1, 2, {:a => 3, :b => 4})

*args gets the same array in both cases.

Now before Daniel goes off on me about behaviors and how it handles
things :wink: ... lets me continue with more evolution happening in the
community:

Behaviors allow Sydney to implement a prototype to these types of
behaviors. However, since they are a separate change to the language I
will leave it to someone else to give an overview of what they do and
don't give use. The interesting part is where Evan is taking his
implementation.

Currently, using a basic array for *args can cause ambiguous calls.
This could be solved by attaching an empty hash at the end of args in
all cases or maybe only certain cases (though something more
dependable is likely to be less work for the human mind). This make it
feel like we are continuously trying to fix the wrong approach to
keyword arguments (I speak for myself only).

After speaking with Evan a little about his plans in more detail, he
discussed that he is considering using a new kind of object in place
of an Array for *. The new class (which I will call Arguments), would
still act like an array (* would still work for expansion into
delegated calls). However, one could now go:

def bar(*rest)
  rest.keywords
  ... etc ... # I am sure we could come up with a suitable interface.
end

This is the exact kind of thing I thing we should be looking for. I
don't know how Matz would feel about it. In fact I still am not sure
myself as I haven't tried using it in examples yet. The point stands
that there might be a good compromise to make between s_ and m_. This
goes a long ways towards that IMO.

Evan says he will be working on an implementation of this for Sydney.
I look forward to testing it live rather than typing code into GMail.
Matz, are there any patches that implement your proposal yet? I would
love to test that out too. If not anyone volunteer? I know I would be
wasting my time trying as I am not that much of an internals wizard.

Thanks,
Brian.

···

On 10/21/05, Brian Mitchell <binary42@gmail.com> wrote:

See also what some other languages have done:
http://www.google.com/search?hs=IOh&hl=en&rls=org.mozilla%3Aen-US%3Aofficial&q=keyword+pos
itional+arguments+rationale&btnG=Search

Warren Seltzer

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

Selon Robert Klemme <bob.news@gmx.net>:

- Mixing both styles for a single method feels not right in Ruby because
it seems more complicated than necessary (just look at the length of the
part of your discussion that deals with this). Stored procedures of RDBMS
can usually be called with parameters given either positional style *or*
keyword style (btw, without a difference in declaration) but not mixed. I
would only allow one of the two approaches.

I'm afraid that would be too restrictive for Ruby. There are already cases (see
Rails for instance, as I learned it in another thread) where methods have
positional arguments followed by hash arguments, which may be advantagedly
replaced with positional arguments followed by keyword arguments. I think it
wouldn't fit with Ruby's dynamic argument passing to make positional and
keyword styles exclude each other.

On the other hand, you are right about one thing: if one is to allow all
arguments to be called either positionally or by keyword (the Sydney approach),
then the positional and keyword styles *must* exclude each other completely.
This is the only way we can prevent confusion.

However, I still think keyword arguments should be explicitly defined as such.

- Personally I'd probably favour an approach which allows only one
calling style at a time. Variables that have no default values declared
will just be nil. Basically this would be the equivalent of this with
nicer syntax:

old:
def foo(h)
  h[:length] * h[:width]
end

foo(:length => 10, :width => 20)

new:
def foo(length, width)
  length * width
end
foo 10, 20
foo width:20, length:10

Although I feel it's a bit too restrictive for Ruby, I could settle for that if
implicit keyword arguments win the debate. But I still prefer explicit ones.

On a side note, has the {a : 10, b : 20} alternative syntax for hashes that I've
seen proposed a long time ago been adopted?

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Daniel Schierbeck wrote:

I still haven't given up on my own style :slight_smile:

def foo(:a, :b, :c = 3, **keys); end

# these should all be valid foo :a => 1, :b => 2 # current style foo a: 1, b: 2 # matz' proposition foo :a = 1, :b = 2 #
my style :smiley:

snip

2) *Method calling* I think matz' style is preferable in many
cases, but i still think the :key = value syntax should be there.

h = Human.new "John Doe", :height = 178, :weight = 65 # metric :wink: car.drive to: LasVegas, :speed = 125

I can't say I can contribute much to the theoretical discussion. I can only say what my first impressions are (up until Brian's email I was a helpless spectator in an incomprehensible discussion).
The :a syntax looks a lot like symbols, which is something I would avoid, in order to avoid paragraphs in documentation explaining that "in method declarations :x means keyword", "when calling methods :a=x is a keyword parameter assignment while :a is a symbol parameter" etc.
Cheers,
V.-

···

--
http://www.braveworld.net/riva

____________________________________________________________________
http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
http://www.freemail.gr - free email service for the Greek-speaking.

Trans wrote:

Why not a Parameters class. This would allow cool things like:

  a = [ :a, :b, :c ]

  def foo(a.to_parm) #as def foo(a,b,c)
    #...
  end

And it would be more useful in the def too (Although maybe this would
be a different class):

  def foo(**parm)
    p parm.arguments
    p parm.named_arguments
    p parm.block.call
  end

  foo(1,2,:a=>3){ "block" }

  => [1,2]
     {:a=>3}
     "block"

Sydney will accomplish this via behaviors:

# This is *not* finalized
def foo(*parm)
   p KeywordBehavior.arguments
   p KeywordBehavior.keyword_arguments
end

Abstracting it out to a generic "Parameter" behavior is something I
hadn't thought of, but knowing Evan, it just might be possible. :slight_smile:

Regards,

Dan

Hi --

Brian Mitchell wrote:

Hello fellow rubyists,

<snip>

Sydney's argument scheme (s_ henceforth) is simple when you want to
reorder the arguments.

def s_f1(a,b,c) ... end
s_f1(b:2, a:1, c:3)

Matz's scheme (m_ from now on) allows this too:

def m_f1(a:,b:,c:) ... end
m_f1(b:2, a:1, c:3)

Ok. Not much difference right away no clear winner.

Ruby has always favored implicitness over explicitness. The fact that
I don't have to do anything explicitly in Sydney makes it the winner
IMHO.

I don't agree. If I write this:

   def meth(a,b,c) ... end

I expect to be "allowed" to rename my parameters at will without
breaking any calling code. I'd even expect to be allowed to change,
say, (a,b,*c) to *args if I felt there was a good reason to.

I don't want my choice of local variable names to be coupled to
calling code. If I offer keywords or hash keys, that means I've
decided that I *do* want to publish those names, and commit to them.
Otherwise I consider them strictly the method's business, not the
caller's.

David

···

On Fri, 21 Oct 2005, Daniel Berger wrote:

--
David A. Black
dblack@wobblini.net

Hi,

1) *Method definition*
     I think named arguments should have the same
     syntax as positional ones, just with a colon
     in front of it.

       def foo(a, b, :c, :d = "bar")

Why with a colon in front of it instead of right after it?

2) *Method calling*
     I think matz' style is preferable in many cases,
     but i still think the :key = value syntax should
     be there.

Why should be?

              matz.

···

In message "Re: A comparison by example of keyword argument styles" on Fri, 21 Oct 2005 18:46:59 +0900, Daniel Schierbeck <daniel.schierbeck@gmail.com> writes:

Hi,

Ruby has always favored implicitness over explicitness. The fact that
I don't have to do anything explicitly in Sydney makes it the winner
IMHO.

Too much implicitness makes code cryptic. Too much explicitness makes
code verbose. It's a matter of balance.

By the way, how do you delegate the whole arguments (including keyword
arguments) in your style? Using KeywordBehavior?

Besides, what would happen for the following code?

  def foo(*args)
    # how do you delegate arguments?
    bar(*args)
  end

  def bar(a,b,c)
    p [a,b,c]
  end

  foo(1,a:2,c:3)

and

  class Foo
    def foo(a,b,c)
      p [a,b,c]
    end
  end
  class Bar<Foo
    # what if argument names differ?
    def foo(d,e,f)
      super
    end
  end
  Bar.new.foo(d:1,e:2,f:3)

              matz.

···

In message "Re: A comparison by example of keyword argument styles" on Fri, 21 Oct 2005 20:12:08 +0900, "Daniel Berger" <djberg96@gmail.com> writes:

Thanks for the reply. This clears the picture up a bit for me.

Brian.

···

On 10/21/05, Daniel Berger <djberg96@gmail.com> wrote:

Brian Mitchell wrote:
> Hello fellow rubyists,

<snip>

> Sydney's argument scheme (s_ henceforth) is simple when you want to
> reorder the arguments.
>
> def s_f1(a,b,c) ... end
> s_f1(b:2, a:1, c:3)
>
> Matz's scheme (m_ from now on) allows this too:
>
> def m_f1(a:,b:,c:) ... end
> m_f1(b:2, a:1, c:3)
>
> Ok. Not much difference right away no clear winner.

Ruby has always favored implicitness over explicitness. The fact that
I don't have to do anything explicitly in Sydney makes it the winner
IMHO.

> Lets examine
> another variation on calling these:
>
> s_f1(1,2,3) # Simple. current behavior.
> m_f1(1,2,3) # Error. No positional arguments.
>
> This shows one point that goes to s_. It makes it easy to use keyword
> args that still have position. However, Matz could consider to allow
> keyword arguments to count as positional arguments and make this
> example go away. It is up to him. +1 for s_ for now. The change would
> force non keyword args to come before keyword args. simple enough.
> Though I still don't see a good reason to share both positional and
> keyword arguments (a good example would be of great help in this
> discussion).
>
> The next example will be a method that takes any number of key-worded arguments:
>
> def m_f2(**keys) ... end
> m_f2(k1: 1, k2: 2, k3: 3)
>
> def s_f2(*args) ... end
> s_f2(k1: 1, k2: 2, k3: 2)
>
> That works but there are some complications that the s_ method starts
> to see (the hidden ugly head). *args now gets an array with hash
> (key-value) or a value with each entry. Ugly.

No, it's a single hash - [{:k1=>1, :k2=>2, :k3=>3}]. In Matz's
implementation, there is no '*args' afaik, only '**keys', which is
{:k1=>, :k2=>2, :k3=>3}.

> Now something internal
> is depending on how someone on the outside (away from the interface)
> called it to see what it gets. I hope this is clear enough for you. +1
> for m_.

I don't understand what you mean here.

> How about mixing positional and keyword args?
>
> def m_f3(p1, p2, k1:, k2:) ... end
> def s_f3(p1, p2, k1, k2) ... end
>
> *_f3(1,2,k1:3, k2: 4)
>
> Not much difference. m_ requires the extra : to be added. This is
> neither a plus or a minus as it can be easily argued both ways. No
> winner. (I will argue it if needed but trust me one can look both
> ways).
>
> How about having a variable number of positional arguments and a set
> number of keys?
>
> def m_f4(*args, a:, b:)
> m_f4(1,2,3,4,5,6,7,8, a:1, b:2) # misleading see bellow.
>
> def s_f4(a, b, *args)
> s_f4(1,2,3,4,5,6,7,8, a:1, b:2) # might have the same problem
>
> The s_ example is nice. It show an intuitive behavior at first but
> depending on implementation you can no longer pull a variable number
> of key paris or you have the same semantic problem that the m_ one
> has.

Huh? What makes you think the number of key pairs is limited?

> If you use * at all that allows any number of arguments of any
> type to be passed. Assuming the latter behavior (needed for *args to
> work with delegation), then neither has any gain. I may be miss
> understanding s_ at this point so please point it out.
>
> How about having both keyword and positional arguments mixed in a
> catch-all with *?
>
> def m_f5(*args)
> m_f5(1,2, a:3, b:4)
>
> def s_f5(*args)
> s_f5(1,2 a:3, b:4)
>
> Well things start to contrast now. For s_ you get: [1,2, { :a => 3}, {
> :b => 4}] if I understand correctly. m_ gives you [1,2, {:a => 3, :b
> => 4}].

No. They are identical.

> I won't debate on which is better in this case. Most of this
> is involved with opinion. However, if you want to look at positionals
> alone and keys alone it is easy with m_ we now have the hash collected
> at the end of *args and can use **keys if we want to. Not a huge plus
> but a point to make things easy. It will minimize boilerplate code on
> a method. I give m_ a +1, you may disregard it if you don't agree.
>
> Now think about the above before we move on. Keep in mind that it is
> not just another way to call a method but gives the method's interface
> a richer meaning (like Matz's slide said).

The ability to inspect positionals alone or keywords alone *will* be
possible, via Behaviors.

> Now for some more concrete examples of usage:
>
> Say we have an object that we create from a class through some special
> method. The some arguments are required while others may or may not be
> there but the circumstances differ. Imagine that the list of
> attributes that can be passed may become quite long so using default
> arguments wouldn't be a very good idea. Or even further, the keys
> might be passed to a second function. This would normally be odd code
> to see but it shows how the nature of the two methods differ by quite
> a bit in real use.
>
> # Untested code. Could contain errors. At least I have an excuse this time.
> class Pet
> def self.m1_create(kind, name, **keys)
> pet = Pet.allocate
> pet.kind = kind
> pet.name = name
> case(kind)
> when :ham
> pet.weight = keys[:weight]
> when :cat
> pet.color = keys[:color]
> when :dog
> pet.color = keys[:color]
> pet.breed = keys[:breed]
> when :ruby
> pet.facets = keys[:facets]
> else
> fail "Uknown kind of pet: #{kind}"
> end
> end
>
> # Same as m1_ but with a different method argument style.
> def self.m2_create(kind:, name:, **keys)
> # Lazy me :wink: They are the same otherwise anyway.
> m1_create(kind,name,**keys)
> end
>
> def self.s_create(kind, name, *args)
> pet = Pet.allocate
> pet.kind = kind
> pet.name = name
> # Messy solution. There is probably a better one.
> get = lambda {|sym|
> args.find(lambda{{}}) {|e|
> e.kind_of? Hash && e[sym]
> }[sym]
> }

<snip>

No, you would use Behaviors. This hasn't been entirely fleshed out
yet, but it will likely look like this.

   def self.s_create(kind, name, *args)
      ...
      keys = KeywordBehavior.keyword_arguments
      ...
   end

So, in that particular example, Sydney takes one more line. I think
there will also be KeywordBehavior.positional_arguments and
KeywordBehavior.arguments. The former would be positional arguments
only, while the latter would be all arguments.

Note that this provides equal or greater flexibility with regards to
delegation, not less, as Matz suggested in one RedHanded comment.

I've committed the test suite to the SVN repository if folks want to
get a clearer idea of how things work (so far). It's
"test_keyword_arguments.rb" under test/ruby.

Regards,

Dan

<quote author="Brian">
After speaking with Evan a little about his plans in more detail, he
discussed that he is considering using a new kind of object in place
of an Array for *. The new class (which I will call Arguments), would
still act like an array (* would still work for expansion into
delegated calls). However, one could now go:

def bar(*rest)
  rest.keywords
  ... etc ... # I am sure we could come up with a suitable interface.
end

This is the exact kind of thing I thing we should be looking for.
</quote>

Ah, now that's a breath of fresh air. And very nice job of combining
new class with Array behavior for backward compatiblity. Intersting
approach. Please let us know how it proceeds.

T.

"Christophe Grandsire" <christophe.grandsire@free.fr> wrote in message

> Positional arguments are *not* part of
> the name of the function. Their position is part of its signature

Their number is part of the signature (rather than their position(s)).

> Keyword arguments, being just a
> part of the name of a method, must be explicitly marked as such
> by the developer, and shouldn't be allowed to be left out by the
> user, just like you can't leave out the name of a method.

I have always preferred this semantics.

It also means keyword argument names must be available through reflection.

Hi,

···

In message "Re: A comparison by example of keyword argument styles" on Sat, 22 Oct 2005 03:08:19 +0900, Brian Mitchell <binary42@gmail.com> writes:

Matz, are there any patches that implement your proposal yet?

Not yet. The behavior was fixed on a night before the keynote. It is
much easier to implement than one in Sydney though.

              matz.