From: Charles Oliver Nutter Date: 2011-10-25T23:06:49+09:00 Subject: [ruby-core:40354] Re: [ruby-trunk - Feature #5474][Assigned] keyword argument On Mon, Oct 24, 2011 at 12:12 AM, Evan Phoenix 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