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