On Mon, Oct 24, 2011 at 12:12 AM, Evan Phoenix <evan / phx.io> wrote:
>> |An alternative design is to treat all parameters as keyword
>> |arguments (as Evan said in [ruby-core:40195]).
>> |
>> | def create_point(x, y, color = "white", size = 1)
>> | p [x, y, color, size]
>> | end
>> | create_point(color: "red", x: 2, y: 3)
>> | #=> [2, 3, "red", 1]
>>
>> It's Python way, and I won't take it.
> What don't you like about this approach? I'd like to know so that hopefully I can formulate an alternative you would like.

The overhead concerns may not be valid. I think it could be
implemented such that the overhead would only be there if called with
a keyword parameter form. Otherwise, all arguments are treated
positionally.

Quick pseudo-algorithm:

call_args = #
if args.kind_of? Hash
  newargs = []
  position_map = method.keyword_to_position
  call_args.each do |key, value|
    case key
    when String
      newargs[position_map[key]] = value
    when Fixnum
      newargs[key] = value
    end
  end
  call_args = newargs
end
method.call_with_args call_args

This would be detectable at compile time; only methods that have
keyword args would do the additional logic of mapping names to
positions.

However, this way of optimizing it does require keyword args always
come after regular positional args. I think that's not too big a leap
to make, since they have to be at the end right now. It does not
require they be specified by the caller in the same order as the
target method, as in MacRuby.

There is a problem with this proposal, though: it could easily break
current code that uses "hash args". For example, a legacy case:

def foo(who, hash)
  ...
end

foo('hello', who: 'world')

This example is slightly contrived, but under current Rubies the "who"
variable in the "foo" method would get 'hello', and under Evan's
proposal it would be 'world'. For this reason I think explicitly
notating keyword parameters in the argument list is better.

> My worry about Yusuke's current proposal is that it requires a Hash be allocated on the caller side to use the feature, which makes the usage of keyword arguments much more heavyweight than normal arguments. This in turn means people will either shy away from them or use them and complain that they're too slow (which could make ruby look bad).

I think the cost of constructing a Hash in Rubinius may be coloring
your thoughts here...and I don't blame you; even though Hash
construction in JRuby is pretty fast, it's not free:

https://gist.github.com/1312815

However, I think much of the Hash-borne overhead could be blunted by
having keyword arg hashes be frozen and list-based. Most of the time
there's no more than a handful of keyword args in use, so having them
be "Hash-like" but backed by a simple associative array would make
them considerably cheaper to construct in all implementations:

https://gist.github.com/a07c93c80dfdea023253

In any case, I don't think there's any reason Yusuke's version would
*require* they be a hash unless the target method *needs* them to be a
hash. More pseudocode:

AT CALL SITE:

call_args = # ...
if call_args.kind_of? Hash
  # map to positional args internally
end
...

IN METHOD PREAMBLE:

if self.keyword_args?
  if self.keyword_rest?
    # unpack positional keyword args with "rest" hash
  else
    # unpack (or not) positional to keyword offsets
  end
end

You'd only pay for the hash if you want it.

- Charlie