Recursive definition of zero, one

Hi all,

Imagine I have a data type like:

struct foo{T,N}
  c::NTuple{N,T}
end

and I want to define the zero function

import Base.zero
zero(s::foo{T,N}) where {T,N} = foo{T,N}(ntuple(i -> zero(T), N))

This allows me to get, for example:

julia> s = foo((1.2,2.2))
foo{Float64, 2}((1.2, 2.2))

julia> zero(s)
foo{Float64, 2}((0.0, 0.0))

But if I try a recursive definition, this fails:

julia> ss = foo((s,s))
foo{foo{Float64, 2}, 2}((foo{Float64, 2}((1.2, 2.2)), foo{Float64, 2}((1.2, 2.2))))

julia> zero(ss)
ERROR: MethodError: no method matching zero(::Type{foo{Float64, 2}})
Closest candidates are:
  zero(::Union{Type{P}, P}) where P<:Dates.Period at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Dates/src/periods.jl:53
  zero(::foo{T, N}) where {T, N} at REPL[3]:1
  zero(::SparseArrays.AbstractSparseArray) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/SparseArrays/src/SparseArrays.jl:55
  ...

I do not understand why it fails. zero(foo{Float64,2}) is perfectly well defined in terms of zero(Float64).

Many thanks!

It’s because you haven’t defined zero(::Type{foo}), but only zero(::foo) instead:

julia> Base.zero(::Type{foo{T,N}}) where {T,N} = foo{T,N}(ntuple(i -> zero(T), N))

julia> zero(ss)
foo{foo{Float64, 2}, 2}((foo{Float64, 2}((0.0, 0.0)), foo{Float64, 2}((0.0, 0.0))))

Your naive recursive definition falls back to calling zero on the type foo{Float64, 2} and not its instance.

2 Likes