Skip to content

Defining zero() seriously #34003

@jiahao

Description

@jiahao

(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).

  1. 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
true

would argue that zero(Union{Float16,Float32}) === Float16(0.0) (it is currently undefined).

  1. A counterargument to the above would be that Int(0) is also a valid choice for zero(Union{Float16,Float32}), since
julia> 0 + Float16(1.0) === Float16(1.0) # works because of type promotion
true

However, 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.

  1. Int(0) appears to be the result of zero(T) wheneverT <: Union{Missing,Number,Complex}, even though it is not always the unique (or even correct) additive identity in T:
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

  1. 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 identity zero(T) + zero(T) === zero(T) is satisfied for all T.

  2. zero(Union{T,S}) === zero(T) when zero(T) + zero(S) === zero(S), taking into account type promotion rules.

  3. If no unique endomorphic zero exists, then zero(T) should be undefined, except for zero(Bool) === false as 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    julepJulia Enhancement ProposalmathsMathematical functions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions