-1

I am trying the code below:

class A
  def initialize foo = nil
    super
  end
end

A.new

When I run it, it raises an error wrong number of arguments (1 for 0) at the super line. Why does this raise an error? I haven't passed any argument to new or super, and am not sure why. If I omit the optional argument signature foo = nil, the error goes away.

The code above with super might not make much sense, but in actual use, A is a subclass of Hash, and I have a block passed to super.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • Super by itself automatically calls the parent with the arguments provided to itself – Netorica Aug 21 '14 at 04:16
  • @Mahan That is right. And I haven't passed any argument. – sawa Aug 21 '14 at 04:17
  • @7stud I don't get what you mean. Particularly, I don't get why you have "a parameter was assigned a default arg" and "an arg was passed in" joined by "or." Those two things are independent of each other. – sawa Aug 21 '14 at 04:21
  • I think you need to use `super()` – Netorica Aug 21 '14 at 04:22
  • @Mahan That would be a way to resolve the error, but I want to know why the error was raised. – sawa Aug 21 '14 at 04:24
  • This makes perfect sense to me. I'm not sure what the confusion is. Regardless of whether you've explicitly passed an argument, the method received an argument, so it gets forwarded up via `super` since you've not used the more explicit `super()`. It's very easy to circumvent this behavior with `super()`; however, if the default were that only explicitly passed arguments were forwarded with `super`, there would be no way to get the current behavior. You'd wind up having to manually forward all your arguments if any of them were optional. – user229044 Aug 21 '14 at 04:43
  • @meagar "the method received an argument" from where? – sawa Aug 21 '14 at 04:48
  • @sawa From the default argument that you specified? – user229044 Aug 21 '14 at 04:49
  • So it seems like the actual explicit call of a method like `new` (or `new()`) doesn't count for the purpose of deciding what arguments are passed, but the way it is received by the method definition counts. – sawa Aug 21 '14 at 04:52
  • 2
    Yes, this is my interpretation. Within the method there is no concept of which arguments were explicitly sent to the method, and which are defaults. I think it would be very surprising if you invoked `initialize(name, age=10); super; end` and only `name` were forwarded via `super`. – user229044 Aug 21 '14 at 04:57
  • 1
    @Jörg W Mittag, For those of us who are too stupid, could you explain to us how the question you linked to is a duplicate answer? In your explanation, please note that the topic of this question pertains to default arguments, and the question you linked to doesn't even employ a default argument. Also consider: if you cannot discern the topic of a question, is it really your place to go around marking questions as duplicates? – 7stud Aug 21 '14 at 16:03
  • 1
    super doesn't care whether a default value was assigned to a parameter variable OR an arg was passed in and assigned to the parameter variable...because super passes parameter variables--not arguments. – 7stud Aug 22 '14 at 04:04

1 Answers1

0

It raises because when you call super without specifying arguments, it calls the parent method with the same arguments that the caller received. In your case, calling just super is the same as calling super(foo), and the Object class (the parent object of A) contains no initializer receiving a parameter. I believe you expect the behavior from this code:

class A
  def initialize foo = nil
    super() # <= note the empty parentesis
  end
end

A.new

It's important to note that an argument with default value is still an argument, even if the default value is nil. A default value only means that callers can call it without specifying the argument, not that the parameter does not exists. For the Ruby VM, your initializer will always have the foo argument.

You can verify this behavior writing the following code in a irb session:

def foo(bar = nil); end
method(:foo).parameters # => [[:opt, :bar]]
Victor
  • 638
  • 4
  • 6
  • Sorry, but your answer does not explain why it still raises an error when `A.new()` is called at the end instead of `A.new` in my original code. – sawa Aug 21 '14 at 06:40
  • It still raises because only the caller context think there is no argument - inside the method there will always be at least one argument. – Victor Aug 21 '14 at 06:52
  • I edited my answer again including info about this. – Victor Aug 21 '14 at 06:57
  • Your "even if the default value is nil" is misleading. It is not relevant. – sawa Aug 21 '14 at 06:58
  • And regarding your last part, it shows that the method is able to distinguish optional arguments from obligatory ones. That does not say anything toward whether optional arguments are counted towards the argument signature. – sawa Aug 21 '14 at 07:01
  • I don't think "even if the default value is nil" is misleading and not relevant. Ruby treats undefined values differently than nil values. A nil parameter is still a parameter, and so it is sent to the superclass when calling super. In this case, calling `A.new` is the same as `A.new(nil)`. I'm sorry, but I don't know how to explain this in a different way. – Victor Aug 21 '14 at 07:32