-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Description
(This issue is a follow-up to #34000 #28854 #31303)
The docstring for zero(x) says
Get the additive identity element for the type of x
However, defining this additive identity precisely seems to be confusing, especially for union types, and in the presence of type promotion rules. The rest of this issue is dedicated to exploring more precisely what the semantics of zero(T) is (to help figure out what exactly the semantics should be).
- In many situations,
zero(::Union)is undefined when it could be defined. Intuitively, one might expect that the presence of multiple zeros might complicate the definition. However, type promotion seems to allow for a uniquely defined result. If we take the definition above seriously, the statements
julia> Float16(0.0) + Float32(1.0) === Float32(1.0) #works for Float32
true
julia> Float16(0.0) + Float16(1.0) === Float16(1.0) #and for Float16
truewould argue that zero(Union{Float16,Float32}) === Float16(0.0) (it is currently undefined).
- A counterargument to the above would be that
Int(0)is also a valid choice forzero(Union{Float16,Float32}), since
julia> 0 + Float16(1.0) === Float16(1.0) # works because of type promotion
trueHowever, the statement above would also imply that Int(0) is a valid choice for zero(Float16).
We can exclude this value by requiring zero() to be endomorphic, i.e. zero(::T) :: T, and by extension zero(T) :: T.
Int(0)appears to be the result ofzero(T)wheneverT <: Union{Missing,Number,Complex}, even though it is not always the unique (or even correct) additive identity inT:
julia> zero(Union{Int,Complex})
0
julia> zero(Union{Real,Complex}) #due to type promotion, see #2
0
julia> 0 + Complex{Bool}(0) === Complex{Bool}(0) # Counterexample: actually Complex{Int}(0), so Int(0) is not an additive identity over Complex
false
julia> Int(0) + Int16(0) === Int(0) #Counterexample: Int16 <: Real but adding Int(0) to it promotes to Int
true
julia> Int(0) + false === Int(0) #Counterexample: Bool <: Real but adding Int(0) to it promotes to Int
true
Proposal
-
zero(T)for non-union types should be defined as the unique additive identity such that
a.zero(T)::T, i.e.zero()is an endomorphism, and
b. The identityzero(T) + zero(T) === zero(T)is satisfied for allT. -
zero(Union{T,S}) === zero(T)whenzero(T) + zero(S) === zero(S), taking into account type promotion rules. -
If no unique endomorphic zero exists, then
zero(T)should be undefined, except forzero(Bool) === falseas a special case.
This definition would preserve the endomorphic property for leaf types, like zero(Float16) === Float16(0.0).
This definition would preserve the result for zero(Bool) === false, to be consistent with current arithmetic semantics of Bool.
This definition would preserve the semantics of #28854 for zero(Missing) === missing.
This definition would define zero(Union{Float16,Float32,Float64}) to be Float16(0.0) (currently undefined).
This definition would define zero(Union{Float16,Int16}) to be Int16(0) (currently undefined).
This definition would change zero(Real) to be false (and similarly in Case 3 above; currently Int(0)); arguably a bug fix.
This definition would change zero(Union{Bool,Int}) to be false (currently Int(0)); arguably a bug fix.