From: Robert Klemme Date: 2012-02-06T19:01:46+09:00 Subject: Re: Error with splat params in method definition On Mon, Feb 6, 2012 at 5:20 AM, Jeremy Bopp wrote: > On 02/05/2012 07:19 PM, Jones Lee wrote: >> I'd like to ask why having: >> >> ``` >> def my_method(*param1, param2 = "default"); end >> ``` >> >> returns >> >> ``` >> SyntaxError: (irb):15: syntax error, unexpected '=', expecting ')' >> ``` This cannot reliably be evaluated because there is an ambiguity. Will my_method(1) invoke with param1 = [] and param2 = 1 or with param1 = [1] and param2 = "default"? No machine can decide this. >> My workaround is explicitly wrap param in brackets like this: >> >> ``` >> def my_method((*param1), param2 = "default"); end >> ``` > > Have you tried calling your method with the workaround with exactly 0 or > more than 2 arguments?  I think you'll find that your workaround doesn't > really work around anything aside from the syntax error. > > Now, I could be wrong about the details, and I believe there are some > subtle changes coming soon with respect to parameter handling for > methods, but I'll see if I can clarify what is going on.  Ruby attempts > to associate method parameters as follows: > > 1) Assign the first parameters given in the method call to all the > positional parameters that do not have defaults defined and report an > error if there are not enough given parameters to do this. > 2) If there are still more given parameters, assign those to the > positional parameters that would otherwise have defaults set. > 3) Assign any remaining parameters that are NOT key-value definitions > for a hash to the splat parameter. > 4) Assign the key-value hash definitions to the final parameter as a > single hash. Not sure whether the order between 3 and 4 is correct or even matters. But that looks like a pretty good description of what's going on - apart from the fact that splash can occur anywhere and hence step one does not only eat leading parameters. > These rules require that method definitions first specify positional > parameters without defaults, followed by positional parameters with > defaults, followed by the splat parameter, followed by a single > parameter that will receive the hash parameters. It's not exactly correct any more with 1.9.*: you can have the splat in other places than the last position: irb(main):007:0> def f(a,*b,c) p a,b,c end => nil irb(main):008:0> f 1 ArgumentError: wrong number of arguments (1 for 2) from (irb):7:in `f' from (irb):8 from /opt/bin/irb19:12:in `
' irb(main):009:0> f 1, 2 1 [] 2 => [1, [], 2] irb(main):010:0> f 1, 2, 3 1 [2] 3 => [1, [2], 3] irb(main):011:0> f 1, 2, 3, 4 1 [2, 3] 4 => [1, [2, 3], 4] > I'm sure that I'm going to get all sorts of corrections about the above, > but that's the gist as I understand things at least. You are correct in that there _are_ limitations. > It appears that you would like to instead specify your positional > parameters (param2) after your splat parameter (param1).  I don't > believe Ruby yet has any facility to handle that in the language, It can do it but not with defaults: irb(main):012:0> def f(*a, b, c) p a, b, c end => nil irb(main):013:0> f 1 ArgumentError: wrong number of arguments (1 for 2) from (irb):12:in `f' from (irb):13 from /opt/bin/irb19:12:in `
' irb(main):014:0> f 1,2 [] 1 2 => [[], 1, 2] irb(main):015:0> f 1,2,3 [1] 2 3 => [[1], 2, 3] irb(main):016:0> f 1,2,3,4 [1, 2] 3 4 => [[1, 2], 3, 4] > but you can emulate it: > > def my_method(*params) >  param1 = params >  param2 = params.size > 1 ? params.pop : "default" >  ... > end Yeah, but who wants to read such code? That will be a heck of a difficult thing to debug. I'd rather stick with standard features because they also allow for automated error checking and reporting. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/