From: Benoit Daloze Date: 2011-10-30T08:08:46+09:00 Subject: [ruby-core:40518] Re: [ruby-trunk - Feature #5474][Assigned] keyword argument Hi, On 23 October 2011 14:53, Yusuke Endoh wrote: > > Issue #5474 has been reported by Yusuke Endoh. > > ---------------------------------------- > Feature #5474: keyword argument > http://redmine.ruby-lang.org/issues/5474 > > Author: Yusuke Endoh > Status: Assigned > Priority: Normal > Assignee: Koichi Sasada > Category: core > Target version: > > > Hello, > > I'm sending a patch for keyword arguments. > > (This feature had been discussed in #5454, but I'm re-creating > �a new ticket because the old ticket was resigtered in ruby-dev) > > > Matz himself proposed this feature. �It is also basically > promised to include the feature in 2.0. �[ruby-core:39837] > I'm planning to commit the patch after it is reviewed by koichi. > > But the detail of the spec is not fixed yet, and may be changed > drastically. > We would like to hear your comments and suggestions, especially, > with a use case and/or an actual experience. > > > > The background of this proposal is that, in the recent Ruby, > the last argument (as a Hash) is often used to pass optional > information. �This feature is intended to aid the style. > > Look an example: > > � �def create_point(x, y, color: "white", size: 1) > � � �# keyword arguments �^^^^^^^^^^^^^^^^^^^^^^^ here! > > � � �p [x, y, color, size] > � �end > > � �create_point(2, 3, color: "red") > � � �#=> [2, 3, "red", 1] > > The caller size is a traditional hash argument notation. > This feature is Hash parsing in the callee side. > > (So it is more suitable to call it "keyword parameter." > �But I use "keyword argument" because everyone calls so.) > > > We can implement the similar behavior in pure Ruby. �However, > this feature is easier to read/write, and richer in the some > aspects: > > - it raises an TypeError when unknown keyword is given > > � �create_point(2, 3, style: "solid") > � � �#=> unknown keyword (TypeError) > > - you can use ** argument to suppress the TypeError and/or > �to get the given hash itself: > > � �def create_point(x, y, color: "white", size: 1, **h) > � � �p [x, y, color, size, h] > � �end > � �create_point(2, 3, style: "solid") > � � �#=> [2, 3, "red", 1, {:style=>"solid"}] > > - it is easily used even when there is a rest argument > > � �def create_point(x, y, *r, color: "solid", size: 1) > � � �... > � �end > > �(a complex and non-essential code is required to > � implement the same behavior in pure Ruby) > > - there is room for optimizing the speed (though I have > �not done any optimization yet) > > > > 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] > > Actually I also like this, but I'm afraid if it is too flexible > and seems difficult to implement and optimize. > > > Thanks, > > -- > Yusuke Endoh > > > -- > http://redmine.ruby-lang.org > > It sounds great! I agree mandatory keyword arguments should be positional arguments and all parameters should not be treated as keyword arguments. I have a few questions/remarks: 1) What is the way to pass keyword arguments ? I would guess `**h` like: def meth(a, **h) other(a, **h) end # => syntax error BTW, using **h in the argument list does not seems to work in some cases for me: def a(**h) end # => syntax error, unexpected tPOW def m(k: nil, **h, &block) end m() # => undefined method `key?' for nil:NilClass 2) I'm a bit dubious about the `**h` syntax to get (and I guess to pass) a Hash though. I know it's the way it's done in Python, but I don't like it (esthetically), especially when it is used to pass the Hash: def meth(a, *r, **h) other(a, *r, **h) end I believe `*args` is appropriate for the rest argument, because the star is the splat operator. I cannot think of any clear logic like that for `**h` except "another rest argument". Also `**` is the power operator, which is unrelated. Something related to `{}`, the literal Hash syntax, would fit better in my opinion. Do you have any idea of an alternate syntax to `**h` ? (Or maybe we should introduce `a, b = **h` as a joke for `a, b = h.values_at(:a, :b)`) 3) What would {Proc,Method,UnboundMethod}#parameters returns for keywords arguments ? def meth(mandatory, optional = nil, *rest, post, keyw: nil, **h, &block) end p method(:meth).parameters Currently: [[:req, :mandatory], [:opt, :optional], [:rest, :rest], [:req, :post], [:block, :block]] Something like: [[:req, :mandatory], [:opt, :optional], [:rest, :rest], [:req, :post], [:key, :keyw], [:keyrest, :h], [:block, :block]] ? 4) I noticed a few problems while experimenting: def a(k: :a, **h) p [k,h] end a(:b, c: :d, e: :f) # => wrong number of arguments (2 for 0) (ArgumentError) It should be "1 for 0" def a(k: :a) p [k,h] end a(r: :a) # => unknown keyword (TypeError) It should say which keyword is missing (and an ArgumentError rather than TypeError, no?). (Of course I do not expect the current patch to pass these details, I just mention them to be sure they will be considered.)