8

I have a method with an optional argument. How can I decide whether the Argument was given or not?

I came up with the following solutions. I am asking this question since I am not entirely satisfied with any of them. Exists there a better one?

nil as default value

def m(a= nil)
    if a.nil?
        ...
    end
end

The drawback with this one is, that it cannot be decided whether no argument or nil was given.

custom NoArgument as default value

class NoArgument
end

def m(a= NoArgument.new)
    if NoArgument === a
        ...
    end
end

Whether nil was given can be decided, but the same problem exists for instances of NoArgument.

Evaluating the size of an ellipsis

def m(*a)
    raise ArgumentError if m.size > 1
    if m.size == 1
        ...
    end
end

In this variant it can be always decided whether the optional argument was given. However the Proc#arity of this method has changed from 1 to -1 (not true, see the comment). It still has the disadvantage of beeing worse to document and needing to manually raise the ArgumentError.

johannes
  • 7,262
  • 5
  • 38
  • 57
  • 1
    The `arity` of all your methods is `-1`. The last solution's only drawback is that you have to manually check that no more than one argument is given and documentation is needed to know what are the arguments. – Marc-André Lafortune Jan 02 '12 at 22:05

2 Answers2

15

Jorg W Mittag has the following code snippet that can do what you want:

def foo(bar = (bar_set = true; :baz))
  if bar_set
    # optional argument was supplied
  end
end
Community
  • 1
  • 1
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • What would be a use case for this? – AndrewF Jan 02 '12 at 21:21
  • 1
    @AndrewF: That question would be better answered by johannes or Jorg W Mittag. – Andrew Grimm Jan 02 '12 at 21:24
  • There are some methods in the core library which behave differently depending on the number of arguments supplied. In effect, they simulate argument-based dispatch. In most Ruby implementations, those methods are implemented in C, C++, Java, C# etc. and they have privileged access to Ruby internals, so they *can* actually simply check the number of arguments. But, if you want to write a compatible method in Ruby, you have to resort to tricks like this. – Jörg W Mittag Jan 03 '12 at 03:42
  • 4
    I think this works the other way around. Your `bar_set` variable is only set when Ruby has to work out the default value for the param, so it's actually set to `true` when `bar` is not given. Perhaps the variable should be named `bar_omitted`? – rodrigo.garcia Apr 18 '13 at 18:24
4

How about

NO_ARGUMENT = Object.new

def m(a = NO_ARGUMENT)
    if a.equal?(NO_ARGUMENT)
        #no argument given
    end
end
Miquel
  • 4,741
  • 3
  • 20
  • 19