#has_arguments?

Messing with optional argument check for the umpteenth time, eg.

def meth(a=Exception)
if a != Exception

else

end
end

Other’s might do:

def meth(*a)
if !a.empty?
a = a.first

else

end
end

Neither of which are very satisfying. So it occurs to me tonight that
we already have #has_block? to see if a block was passed. So how about
a #has_arguments? to query if any arguments have been passed. So
then…

def meth(a=default)
if has_arguments?

else

end
end

Ah… now that would be nice.

def meth(*a)
if !a.empty?
a = a.first

else

end
end

Maybe morph this to

def meth(*a)
if has_arguments? a

else

end
end

?
-r

On Tue, Aug 18, 2009 at 11:37 PM, Intransition[email protected]
wrote:

Other’s might do:
Neither of which are very satisfying. So it occurs to me tonight that
end

Ah… now that would be nice.

I’ve never felt the need for this.

Normally I just make the default value for an optional parameter nil,
and check for nil.

If nil is a valid value, then I’d do something like

class A

MissingArgument = Object.new

def meth(a=MissingArgument)
if a == MissingArgument
else
end
end
end


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

On Aug 19, 9:09 am, Rick DeNatale [email protected] wrote:

end
end

Ah… now that would be nice.

I’ve never felt the need for this.

Normally I just make the default value for an optional parameter nil,
and check for nil.

If nil is a valid value, then I’d do something like

Right, it precisely the times when nil is a valid value that it is a
problem.

class A

MissingArgument = Object.new

def meth(a=MissingArgument)
if a == MissingArgument
else
end
end
end

Hmm… doesn’t that indicate a need? :wink:

And yes, I’ve done that before too, but it falls into the realm of my
first example. Exception is typically just as good as MissingArgument,
and that way you don’t need a stray object that exists just to exist.
But in either case it is still a (albeit minor) jerry-rig.

What we have here is a nail, and I’d much rather have a nice hammer
than to keep using this damn rock.

On Aug 19, 2009, at 8:08, Trans [email protected] wrote:

Normally I just make the default value for an optional parameter nil,
and check for nil.

If nil is a valid value, then I’d do something like

Right, it precisely the times when nil is a valid value that it is a
problem.

class X
NONE = Object.new

def blah(a = NONE)
# …
end
end

Suffices for nil as valid value

On Aug 19, 7:53 am, Roger P. [email protected] wrote:

def meth(*a)
if has_arguments? a

else

end
end

Either way is fine. The defined method interface doesn’t matter.
#has_arguments? would simply report false if a call to the method
passed no arguments. If the interface definition itself does not allow
for no arguments, eg. ‘def meth(a)’, then #has_arguments? would simply
never return false.

T.

On Thu, 20 Aug 2009, Trans wrote:

 end

Either way is fine. The defined method interface doesn’t matter.
#has_arguments? would simply report false if a call to the method
passed no arguments. If the interface definition itself does not allow
for no arguments, eg. ‘def meth(a)’, then #has_arguments? would simply
never return false.

T.

I’m still not convinced this is necessarily needed, but, how about this
for a first stab (still looking at whether there’s a way to not need the
explicit call to binding) :

class Object
def has_arguments?(b)
vars = eval(“local_variables”,b)
return false if vars.length == 0
vars.each do |v|
return false if eval("#{v}.nil?",b)
end
return true
end
end

class Foo
def initialize(first=nil)
if (has_arguments? binding)
puts “We’re defined”
else
puts “Not!”
end
end

def no_args
if (has_arguments? binding)
puts “We’re defined”
else
puts “Not!”
end
end

def mandatory_arg(a)
if (has_arguments? binding)
puts “We’re defined”
else
puts “Not!”
end
end
end

Matt

On Aug 18, 11:37 pm, Intransition [email protected] wrote:

Other’s might do:
Neither of which are very satisfying. So it occurs to me tonight that
end

Ah… now that would be nice.

Here’s a little trick I learned (very hacky):

def meth(a=(no_argument=‘default’))
if no_argument
‘nothing given’
else
“we got #{a.inspect}”
end
end

Your default argument can’t be falsy (but you could use something
truey and use your falsy value in its place). Anyway:

036:0> meth
“nothing given”
037:0> meth(nil)
“we got nil”
038:0> meth(‘whatever’)
“we got “whatever””
039:0> meth
“nothing given”

I can’t vouch for JRuby or Ruby 1.9 or Rubinius or IronRuby for this
one (work’s a strictly 1.8.6 kind of place).

-Chris

As a followup, has_arguments? could be written as:

def has_arguments?(&b)
vars = eval(“local_variables”,b.binding)
return false if vars.length == 0
vars.each do |v|
return false if eval("#{v}.nil?",b)
end
return true
end
end

then you’d call it like:

if has_arguments? {} #note the empty block
something
end

Matt

Matthew K. Williams wrote:

end

then you’d call it like:

if has_arguments? {} #note the empty block
something
end

Matt

Using #local_variables won’t work for this, will it?

def has_arguments?(&b)
vars = eval(“local_variables”,b.binding)
return false if vars.length == 0
vars.each do |v|
return false if eval("#{v}.nil?",b)
end
return true
end

def foo(*a)
if has_arguments? {} #note the empty block
p a
else
puts “no args”
end
x=3 # <-- N.B.
end

foo()
foo(1,2,3)

END

Output:

no args
no args

On Thu, 20 Aug 2009, Joel VanderWerf wrote:

end
Matt
end
foo()
foo(1,2,3)

END

Output:

no args
no args

Good point… I didn’t think of that case – can you suggest a
solution?

On Tue, Aug 18, 2009 at 10:37 PM, Intransition[email protected]
wrote:

Messing with optional argument check for the umpteenth time, eg.

 def meth(a=Exception)
  if a != Exception
   …
  else
   …
  end
 end

def meth(a=(defaults=true;Exception))
if !defaults

else

end
end

  • charlie

Thomas S. wrote:

So it occurs to me tonight that
we already have #has_block? to see if a block was passed. So how about
a #has_arguments? to query if any arguments have been passed. So
then…

def meth(a=default)
if has_arguments?

else

end
end

Ah… now that would be nice.

But it doesn’t solve the general case of

def meth(a, b, c=default)

end

You could have argument_size I suppose. I’ve never felt the need.

Generally the only time I’ve done this is with methods which have a hash
of options, in which case ‘has_key?’ does the job.

On Wed, Aug 19, 2009 at 3:40 PM, Chris S.[email protected] wrote:

Your default argument can’t be falsy (but you could use something
truey and use your falsy value in its place). Â Anyway:

Yeah, my version avoids the falsy issue…but yours is shorter.

Either way…works fine in JRuby and 1.9.

  • Charlie

2009/8/19 Intransition [email protected]:

Messing with optional argument check for the umpteenth time, eg.

def meth(a=Exception)
if a != Exception

else

end
end

My 0.02 EUR: if your method’s behavior changes depending on argument
or argument types chances are that you may rather want different
methods.

As a side note: I see this sometimes

def m(a = nil)
@a = a.nil? ? 123 : a
end

which is of course silly. The same would be much better expressed as

def m(a = 123)
@a = a
end

There is no need for additional optional logic - at least in these
simple cases.

end
end

Ah… now that would be nice.

It may be useful in some cases but since you can always do the “trick”
with “*” I am not sure whether we really need this. I always found
block_given? a bit awkward since it looks like a method of self but is
rather a quick way to get at information about the argument list. The
only reason I use it at times is the fact that the combination m(&b)
b.call is slower than the combination m() yield.

I’m undecided really.

Kind regards

robert

Robert K. wrote:

As a side note: I see this sometimes

def m(a = nil)
@a = a.nil? ? 123 : a
end

which is of course silly. The same would be much better expressed as

def m(a = 123)
@a = a
end

No, it’s not the same. Consider:

def foo(a = nil)
m(a)
end

foo()

It saves replicating your default values all over the place, which isn’t
very DRY.

Or you could more simply write

def m(a = nil)
@a = a || 123
end

On Thu, Aug 20, 2009 at 8:03 AM, Brian C.[email protected]
wrote:

No, it’s not the same. Consider:

def foo(a = nil)
m(a)
end

foo()

It saves replicating your default values all over the place, which isn’t
very DRY.

I didn’t take Robert as meaning that the above is silly.

Or you could more simply write

def m(a = nil)
@a = a || 123
end

But, unless I’m missing something subtle, THIS is what Robert was
calling silly, since as far as I can tell, this

def m(a=123)
@a = a
end

Has exactly the same effect, and seems much clearer.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

2009/8/20 Brian C. [email protected]:

@a = a
end

No, it’s not the same.

Can you explain what the differences are?

Consider:

def foo(a = nil)
m(a)
end

foo()

I do not see the connection of this to what I posted.

It saves replicating your default values all over the place, which isn’t
very DRY.

I don’t see how this makes me replicate my default values all over the
place. My proposed solution just changes how a method internally
works, there is no effect on the interface.

Or you could more simply write

def m(a = nil)
@a = a || 123
end

I fail to see how this is simpler than

def m(a = 123)
@a = a
end

I’m in line with Rick: either I must be missing something - or you.
:slight_smile:

Cheers

robert

Rick Denatale wrote:

But, unless I’m missing something subtle, THIS is what Robert was
calling silly, since as far as I can tell, this

def m(a=123)
@a = a
end

Has exactly the same effect

I don’t think so:

prog1.rb

def m(a=123)
@a = a
end
def a
@a
end

def foo(x=nil)
m(x)
end
foo
puts “The result is #{a.inspect}” # shows nil

prog2.rb

def m(a=nil)
@a = a || 123
end
def a
@a
end

def foo(x=nil)
m(x)
end
foo
puts “The result is #{a.inspect}” # shows 123

2009/8/20 Robert K. [email protected]:

def m(a = 123)
m(a)
end

foo()

I do not see the connection of this to what I posted.

Argh! Now I see what you mean. If you use nil as universal “absence”
marker then you have to declare the default only inside the method
when doing the a.nil? logic because you will pass it through from
calling methods.

I only considered a single method and did not have nested calls in
mind. Do you believe that these are common enough to warrant making
this a common pattern?

Btw, we can also do this:

def foo(*a)
bar(*a)
end

def bar(a = 123)
@a = a
end

Kind regards

robert