Interface for `Number`


I recently ran into an issue overriding zero for my own Number:

julia> immutable Infinity <: Number end

julia> = 0

julia> import Base: +

julia> +(::Infinity,::Int) = Infinity()
+ (generic function with 181 methods)

julia> reduce(+,[Infinity(),Infinity()])
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Infinity
This may have arisen from a call to the constructor Infinity(...),
since type constructors fall back to convert methods.
 [1] _mapreduce(::Base.#identity, ::Base.#+, ::IndexLinear, ::Array{Infinity,1}) at ./reduce.jl:260
 [2] reduce(::Function, ::Array{Infinity,1}) at ./reduce.jl:321

The issue appears to be that zero{T<:Number}(::T) is expected to return a T.

Is this a bug, or is this part of the Number “Interface”? Is the Number “Interface” written down anywhere?


I’d say it’s part of the interface. If you can’t expect that zero(::T) returns a type T, then lots of codes would be potentially type-unstable.

I don’t think this interface is written down anywhere, but it should get documented better.


OK that’s reasonable. Yes, if built in types have an interface that Base Julia assumes (like for Number) it should be written down.

Even better, there should be a testnumberinterface(Infinity) to test that all the contracts that Base expects are satisfied for my type. (Also, testiterator, testabstractarray, etc.)


Is this new in v0.6?

For v0.5, the following works:

immutable Infinity <: Number end = 0

import Base: +
+(::Infinity,::Int) = Infinity()
+(::Infinity,::Infinity) = Infinity()



Yes it’s 0.6 (sorrry, should have mentioned this was a surprising new behaviour of something that worked fine in 0.5)


Note that the precise problem is not that zero() returns a different type, it’s that the value returned by zero() cannot be converted to Infinity. Defining this (incorrect) method fixes the issue:
Base.convert(::Type{Infinity}, x::Integer) = Infinity().

This sounds like an unintentional restriction to me. It could be worth filing a bug. Ideally what should happen is that either the error would only be thrown when the collection is empty (i.e. when zero must be used), or we would accept a type instability by returning 0 disregarding its type. But these are complex issues.


Created an issue: