Understanding 'MethodError'

Please consider my very first Julia function below.
It throws a “MethodError: no method matching zero(::Type{Any})”
and gives a list of three “Closest candidates”.
I do not understand how I can correct this error. Any help appreciated.

``````function b(u::Int, o::Int, t::Int)
if u == -o
return 1
end
if t == 0
return sum(b(u-j, o+j-1, (t+1) % 2) for j in 1:u)
end
return sum(b(u+j-1, o-j, (t+1) % 2) for j in 1:o)
end
function B(n::Int)
return b(n, 0, 0)
end
B(5)
``````

Your function ends up trying to sum over an empty collection, which seems like it should be fine – the answer is zero, right? – until you ask the question “which zero?” The `sum` function answers that question by calling `zero(Any)` which is not defined. You can avoid this problem by avoiding the empty cases, e.g. by adding explicit recursion cases instead. E.g.:

``````julia> function b(u::Int, o::Int, t::Int)
if t == 0 && u == 0 || o == 0
return 0
end
if u == -o
return 1
end
if t == 0
return sum(b(u-j, o+j-1, (t+1) % 2) for j in 1:u)
end
return sum(b(u+j-1, o-j, (t+1) % 2) for j in 1:o)
end
b (generic function with 1 method)

julia> function B(n::Int)
return b(n, 0, 0)
end
B (generic function with 1 method)

julia> B(5)
0
``````

I’m not sure if that was the answer you were expecting, but that’s what I got.

1 Like

“…the answer is zero, right?”

No. As you can see from the almost identical
Python function below I expect

``````[B(n) for n in (0..6)]
[1, 1, 1, 2, 5, 16, 61]

def b(u, o, t):
if u ==-o: return 1
if t == 0: return sum(b(u-j, o+j-1, (t+1) % 2) for j in (1..u))
return sum(b(u+j-1, o-j, (t+1) % 2) for j in (1..o))

B = lambda n: b(n, 0, 0)
``````

By “the answer is zero, right?” I don’t think Stefan was referring to your overall function, but rather to the question of summing over an empty collection.

The version Stefan posted returns 0 in more cases than your original. You want something more like

``````function b(u::Int, o::Int, t::Int)
if u == -o
return 1
end
if t == 0
return u == 0 ? 0 : sum(b(u-j, o+j-1, (t+1) % 2) for j in 1:u)
end
return o == 0 ? 0 : sum(b(u+j-1, o-j, (t+1) % 2) for j in 1:o)
end
``````

“You want something more like …”

Yes, this is what I was looking for. Basically it takes care that
the sum over an empty set is 0, which is common practice.

Still I wonder why in this case the “sum function answers
by calling zero(Any) which is not defined” since the
function b(u::Int, o::Int, t::Int) specifies the types of
the parameters. If I had written only written b(u, o, t)
I would understand it immediately. In other words: Where
does this ‘Any’ in ‘zero(Any)’ comes from?

I think there were some type inference issues limiting the compiler’s ability to correctly guess the element type of generators on 0.5. On Julia 0.6-dev I get a different error, `ArgumentError: reducing over an empty collection is not allowed`. I’m not sure whether the PR that fixed that is backportable (or if it has been marked for backporting) but should find out shortly as I work through that list.

2 Likes

You could also change your `sum(generator)` calls to `reduce(+, 0, generator)` which specifies the return value for empty collections. Looks like that works fine on both 0.5 and 0.6-dev.

3 Likes