From: Robert Klemme Date: 2013-02-07T19:20:53+09:00 Subject: Re: Implementing DRY with a function call On Thu, Feb 7, 2013 at 1:39 AM, Rob Marshall wrote: > Hi Robert, > > At present I'm using the approach I mentioned before, but let me try and > explain my confusion... OK, I'll try to clarify where I can. > Every other language I've worked in, C, Perl, Python, Java...the way you > call a function with arguments is: func(arg1, arg2, arg3). In Python you > can have variable args and keyword args specified as (*args, **kwargs), > but in general you define the arguments that a function expects, and > then pass those arguments between parentheses. So the idea that I could > do: > > func(arg1,arg2,arg3) { block } > > And a) Not get a syntax error, and b) Be able to define the parameters > for the function as: > > def func(*args) > yield > end > > and not only get the args specified between the parens in *args, but > also still pass the block and "yield" it, is a very new, and foreign, > concept for me. Well, every language has its features which one needs to get used to. :-) A block is special since Ruby will happily pass it to *every* method called even though the method does not do anything with it. And it does not need any declaration. Unless you want to store it somewhere where the &argument_name syntax is needed (see my last post). But even that does not affect errors you are seeing: > What I find a bit confusing about Ruby is that, given: > > def func(*args) > args.each { |a| puts a } > yield > end > > Then do the following: > >>> func "this", "is", "a", "test", return_value("something") > this > is > a > test > This is the returning value: something > LocalJumpError: no block given > from (irb):108:in `func' > from (irb):110 > > This one is sort-of obvious since the function return_value [...] > is evaluated prior to being passed as an argument to func. Exactly. Not a function is passed but the result of evaluating a function. Nothing special here. That would be the same as in other languages. And it doesn't really make a difference in what position you have the method call. Same as in other languages. I think you're making it more difficult for you to understand than necessary by introducing #return_value here: this is just an expression as all the literals you have in the invocation above. > But since there's no block, I get the LocalJumpError. Yes, that's caused by invoking yield in absence of a block passed. Btw you can check that with #block_given? irb(main):001:0> def f; block_given? end => nil irb(main):002:0> f => false irb(main):003:0> f {} => true >>> func "this", "is", "a", "test", { return_value("something") } > SyntaxError: compile error > (irb):111: odd number list for Hash > from (irb):111 > > This is is also somewhat obvious because it isn't treating the {} as > defining a block but attempting to define a hash...That's because I > haven't "formally" defined the arguments, so the interpreter assumes > everything is supposed to be an argument and not a block. No, this is wrong. That has absolutely nothing to do with how you defined the method! This is a syntax error. And it has to do with different precedence of {} and do end. You don't even have to define the method for getting those errors - it's all about how the call is written: $ ruby -ce 'func "this", "is", "a", "test", { return_value("something") }' -e:1: syntax error, unexpected '}', expecting tASSOC $ ruby -ce 'func "this", "is", "a", "test" { return_value("something") }' -e:1: syntax error, unexpected '{', expecting $end func "this", "is", "a", "test" { return_value("something") } ^ $ ruby -ce 'func("this", "is", "a", "test") { return_value("something") }' Syntax OK $ ruby -ce 'func "this", "is", "a", "test" do return_value("something") end' Syntax OK For Ruby at the calling location all methods have the same signature *at compile time*. Issues like wrong number of arguments etc. are only detected at runtime! $ ruby -ce 'def f(a) p a end; f(); f(1); f(1,2)' Syntax OK $ ruby -e 'def f(a) p a end; f(); f(1); f(1,2)' -e:1:in `f': wrong number of arguments (0 for 1) (ArgumentError) from -e:1:in `
' $ ruby -e 'def f(a) p a end; f(1); f(1,2)' 1 -e:1:in `f': wrong number of arguments (2 for 1) (ArgumentError) from -e:1:in `
' So, to put it differently, for Ruby all methods have the same signature f(*args, &block) when it looks at them on calling sites at compile time. >>> func("this", "is", "a", "test") { return_value("something") } > this > is > a > test > => "This is the returning value: something" > > This is still the odd one for me that now (sort-of) makes sense but > would NOT be allowed in any language I've used in the past. So it's > going to take some getting used to... So far your issues have been only syntax errors and they are totally unrelated to how you define the method. I think that's where you took the wrong turn: Ruby is not checking whether argument lists are properly passed or whether a method uses a block (all will _accept_ a block!) at compile time. Only at runtime Ruby will notice - wrong number of arguments - using a block when none was passed Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/