JuliaServices / AutoHashEquals.jl

A Julia macro to add == and hash() to composite types.
Other
58 stars 12 forks source link

hash(Type) is not stable across runs #39

Closed gafter closed 1 year ago

gafter commented 1 year ago

hashing a type is not fast. That's why some people cache it in a static map. This code should either avoid hashing a type, or should store it in a static map.

gafter commented 1 year ago

Oh, the hash of a type probably isn't stable between julia runs, as it is based on object identity. Which means that an object with a precomputed hash that involves the hash of a type likely will not get the same hash code as another "equivalent" object created in a different run of julia.

gafter commented 1 year ago

It actually is stable within a particular build of Julia it seems.

gafter commented 1 year ago

This might be better:

# compute the hash of any given type by hashing the fully qualified name of that
# type.  But do it only once, and cache the result in a generated function.
@generated function _hashed_type_name(x::Type{T}) where {T}
    hash(string(T))
end

See discussion on the Julia slack.

gafter commented 1 year ago

I wonder how Union types decide what order to print their members in.

julia> Union{Union{A{String}, A{Int}}, Int}
Union{Int64, A{Int64}, A{String}}

julia> Union{Int64, A{Int64}, A{String}}
Union{Int64, A{Int64}, A{String}}
gafter commented 1 year ago

@NHDaly points out that can change even during a single run due to aliasing. This, then:

@generated function _hashed_type_name(x::Type{T}) where {T}
    iobuf = IOBuffer()
    show(IOContext(iobuf, :compact => false), T) # This IOContext avoids alias search.
    type_string = String(resize!(iobuf.data, iobuf.size))
    hash(type_string)
end

(https://github.com/RelationalAI/raicode/blob/772b0918321e63d66a9ef290897b7c6d91bf72ee/packages/MurmurHash3F/src/MurmurHash3F.jl#L255-L262)

gafter commented 1 year ago
julia> module A; struct B; x; end; end
Main.A

julia> Base.hash(A.B)
0xedeca5aeea4dd690

julia> module A; struct B; x; end; end
WARNING: replacing module A.
Main.A

julia> Base.hash(A.B)
0x11bcb3aac1326f85
gafter commented 1 year ago

We have https://github.com/JuliaServices/AutoHashEquals.jl/pull/44 to address this for clients who care.