Trying to understand the order methods will be recognized in the case of overloading.

I have this test case:

``````class Foo
def bar(a, b, c)
"bar1"
end

def bar(a, b, c = 1)
"bar2"
end

def bar(a, b = 1, c = 2)
"bar3"
end

def bar(a = 1, b = 2, c = 3)
"bar4"
end

def boo(a) # restriction 4 (least restrictive, but its never used actually)
"boo1"
end

def boo(a : String) # restriction 1 (most restrictive)
"boo2"
end

def boo(a : String | Int32) # restriction 2
"boo3"
end

def boo(a : String | Int32 | Bool) # restriction 3 <- this will be defined last, and so will get used in all cases?
"boo4"
end
end

puts Foo.new.bar(1, 2, 3)
puts Foo.new.bar

puts Foo.new.boo(12)
puts Foo.new.boo(true)
``````

which produces:

``````bar4
bar4
boo3
boo4
``````

okay, now if I switch things around, I would still expect the same result.

but instead I get an error (wrong number of params) unless the definition with all three default params of bar is last.

More over, if I switch the two definitions of boo around it prints boo4 twice. I.e. it doesnâ€™t seem that the ordering is actually doing anything.

``````class Foo
def bar(a, b, c)
"bar1"
end

def bar(a, b, c = 1)
"bar2"
end

def bar(a = 1, b = 2, c = 3)
"bar4"
end

def bar(a, b = 1, c = 2)
"bar3"
end

def boo(a) # restriction 4 (least restrictive, but its never used actually)
"boo1"
end

def boo(a : String) # restriction 1 (most restrictive)
"boo2"
end

def boo(a : String | Int32 | Bool) # restriction 3 <- this will be defined last, and so will get used in all cases?
"boo4"
end

def boo(a : String | Int32) # restriction 2
"boo3"
end
end

puts Foo.new.bar(1, 2, 3)
# puts Foo.new.bar <- this no won't work

puts Foo.new.boo(12)
puts Foo.new.boo(true)
``````

produces

``````bar3
boo4
boo4
``````

Hi there!

Good questions. I took the liberty of editing your post to include code sections, otherwise it was very hard to understand.

``````  def bar(a, b, c)
"bar1"
end

def bar(a, b, c = 1)
"bar2"
end
``````

The second overload overrides the first one. Give that you are defining a method with 2 required arguments and one optional, if you pass 2 arguments it will call that method. If you pass 3, it will call that method too!

I mean, if you pass three arguments, if the first overload is chosen by the compiler, whatâ€™s the point of having a default value for the third argument if itâ€™s never going to be used?

The same logic applies to the following overloads:

``````  def bar(a, b = 1, c = 2)
"bar3"
end
``````

now you have two optional arguments, that overrides the previous overload.

``````  def bar(a = 1, b = 2, c = 3)
"bar4"
end
``````

Same. This is the only overload that remains.

``````def boo(a) # restriction 4 (least restrictive, but its never used actually)
"boo1"
end

def boo(a : String) # restriction 1 (most restrictive)
"boo2"
end

def boo(a : String | Int32) # restriction 2
"boo3"
end

def boo(a : String | Int32 | Bool) # restriction 3 <- this will be defined last, and so will get used in all cases?
"boo4"
end
``````

When you have `String | Int32` vs `String | Int32 | Bool`, if you pass an `Int32`, which one should win? Itâ€™s not clear. Thatâ€™s why the compiler just leaves them in order and when you invoke the method with `Int32` it will check each of them. Of course the first one wins.

When you pass bool, the last one is the one that has the `Bool` restriciton.

The first overload without restrictions will be called if you pass a different type, like Char:

``````Foo.new.bar('a') # => "boo1"
``````

okay, now if I switch things around, I would still expect the same result.

This is incorrect. The order matters. In case thereâ€™s no clear â€śthis is more strict than thisâ€ť, the compiler will try to match overloads in order.

In general, my advice is to avoid such code because itâ€™s confusing for you and for everyone. Thereâ€™s really no need to define such overloads.

@asterite Thanks for the reply, and sorry for the bad formatting (I was typing the original message on a cell phone :-) )

The reason I am asking these questions is that I am working away on a Crystal -> JS compiler, and so need to understand these semantics. Even though as you say in real life these kind of overloads should be avoided, its important that the code runs the same regardless of the code gen backend!

Anyway after digging a bit more I think I have better clarity on what is going on here with the â€śrestrictionsâ€ť and â€śrestriction_ofâ€ť methods.

Thanks!

1 Like