API

StableHashTraits.stable_hashFunction
stable_hash(x, context; alg=sha256)
stable_hash(x; alg=sha256, version)

Create a stable hash of the given objects. As long as the context remains the same, this hash is intended to remain unchanged across julia versions. The built-in context is HashVersion{N}, and if you specify a version, this is equivalent to explicitly passing HashVersion{version}.

The version must be explicitly specified: if a new hash version is provided in a future release, your result will not change.

You customize how hashes are computed using transformer.

To change the hash algorithm used, pass a different function to alg. It accepts any sha related function from SHA or any function of the form hash(x::AbstractArray{UInt8}, [old_hash]).

The context value gets passed as the second argument to transformer, see Using Contexts for details.

See Also

transformer

source
StableHashTraits.HashVersionType
HashVersion{V}()

The default hash_context used by stable_hash. There are currently four versions (1-4). Version 4 should be favored when at all possible. Version 1 is the default version, so as to avoid changing the hash computed by existing code.

By explicitly passing this hash version in stable_hash you ensure that hash values for these fallback methods will not change even if new hash versions are developed.

source
StableHashTraits.transformerFunction
StableHashTraits.transformer(::Type{T}, [context]) where {T}

Return Transformer indicating how to modify an object of type T before hashing it. Methods with a context are called first, and if no method for that type exists there is a fallback that calls the method without a context. Users can therefore implement a method with a context object to customize transformations for that context only, or a single argument method when they wish to affect the transformation across all contexts that don't have a context specific method.

source
StableHashTraits.TransformerType
StableHashTraits.Transformer(fn=identity, result_method=nothing;
                             hoist_type=StableHashTraits.hoist_type(fn))

Wraps the function used to transform values before they are hashed. The function is applied (fn(x)), and then its result is hashed according to the trait @something result_method StructType(fn(x)).

The flag hoist_type indicates if it is safe to hoist type hashes outside of loops. If set to true your has will be computed more quickly. However, this hoisting is only valid when the pre-transformed type is sufficient to disambiguate the hashed values that are produced downstream AND when the post-transformed types that are concrete depend only on pre-transformed types that are themselves concrete.

Use `hoist_type=true` with care

It is easy to introduce subtle bugs that occur in rare edge cases when using hoist_type=true. Refer to Optimizing Custom Transformers for a detailed discussion and examples of when you can safely set hoist_type=true. It is better to use a pre-defined function such as pick_fields or omit_fields.

See Also

transformer

source
StableHashTraits.pick_fieldsFunction
pick_fields(x, fields::Symbol...)
pick_fields(x, fields::NTuple{<:Any, Symbol})
pick_fields(fields::Symbol...)
pick_fields(fields::NTuple{<:Any, Symbol})

Return an object including fields from the fields of x, as per getfield. Curried versions exist, which return a function for selecting the given fields.

This function differs from returning a named tuple of fields (e.g. x -> (;x.a, x.b)) in that it does not narrow the types of the returned fields. A field of type Any of x is a field of type Any in the returned value. This ensures that pickfields can be safely used with `hoisttypeof [Transformer`](@ref).

source
StableHashTraits.omit_fieldsFunction
omit_fields(x, fields::Symbol...)
omit_fields(x, fields::NTuple{<:Any, Symbol})
omit_fields(fields::Symbol...)
omit_fields(fields::NTuple{<:Any, Symbol})

Return an object excluding fields from the fields of x, as per getfield. Curried versions exist, which return a function for selecting the given fields.

This function differs from returning a named tuple of fields (e.g. x -> (;x.a, x.b)) in that it does not narrow the types of the returned fields. A field of type Any of x is a field of type Any in the returned value. This ensures that pickfields can be safely used with `hoisttypeof [Transformer`](@ref).

source
StableHashTraits.TransformIdentityType
StableHashTraits.TransformIdentity(x)

Signal that the type x should not be transformed in the usual way, but by hashing x directly. This is useful when you want to hash both x the way it would normally be hashed without a specialized method of transformer along with some metadata. Without this wrapper, returning (metadata(x), x) from the transforming function would cause an infinite regress (adding metadata(x) upon each call).

Example

struct MyArray <: AbstractVector{Int}
    data::Vector{Int}
    meta::Dict{String, String}
end
# other array methods go here...
function StableHashTraits.transformer(::Type{<:MyArray})
    return Transformer(x -> (x.meta, TransformIdentity(x)); hoist_type=true)
end

In this example we hash both some metadata about a custom array, and each of the elements of x

source
StableHashTraits.transform_typeFunction

transform_type(::Type{T}, [context])

The value to hash for type T when hashing an object's type. Users of StableHashTraits can implement a method that accepts one (T) or two arguments (T and context). If no method is implemented, the fallback transform_type value uses StructType(T) to decide how to hash T; this is documented under What gets hashed?.

Any types returned by transform_type has transform_type applied to it, so make sure that you only return types when they are some nested component of your type (do not return T!!)

This method is used to add additional data to the hash of a type. Internally, the data listed below is always added, outside of the call to transform_type:

  • fieldtypes(T) of any StructType.DataType (e.g. StructType.Struct)
  • eltype(T) of any StructType.ArrayType or StructType.DictType or AbstractRange

These components of the type need not be returned by transform_type and you cannot prevent them from being included in a type's hash, since otherwise the assumptions necessary for efficient hash computation would be violated.

Examples

Type Parameters

To include additional type parameters in a type's hash, you can overwrite transform_type

struct MyStruct{T,K}
    wrapped::T
end

function StableHashTraits.transform_type(::Type{<:MyStruct{T,K}})
    return "MyStruct", K
end

By adding this method for type_structure both K and T will impact the hash, T because it is included in fieldtypes(<:MyStruct) and K because it is included in type_structure(<:MyStruct).

If you do not own the type you want to customize, you can specialize type_structure using a specific hash context.

using Intervals

StableHashTraits.@context IntervalEndpointsMatter

function HashTraits.type_structure(::Type{<:I}, ::IntervalEndpointsMatter) where {T, L, R, I<:Interval{T, L, R}}
    return (L, R)
end

context = IntervalEndpointsMatter(HashVersion{4}())
stable_hash(Interval{Closed, Open}(1, 2), context) !=
    stable_hash(Interval{Open, Closed}(1, 2), context) # true

See Also

transformer @context

source
StableHashTraits.module_nameof_stringFunction
module_nameof_string(::Type{T})
module_nameof_string(T::Module)
module_nameof_string(::T) where {T}

Get a (mostly!) stable name of T. This is a helpful utility for writing your own methods of StableHashTraits.transform_type and StableHashTraits.transform_type_value. The stable name includes the name of the module that T was defined in. Any uses of Core are replaced with Base to keep the name stable across versions of julia. Anonymous names (e.g. module_nameof_string(x -> x+1)) throw an error, as no stable name is possible in this case.

A type's module often changes

The module of many types are considered an implementation detail and can change between non-breaking versions of a package. For this reason uses of module_nameof_string must be explicitly specified by user of StableHashTraits. This function is not used internally hor HashVersion{4} for types that are not defined in StableHashTraits.

source
StableHashTraits.is_orderedFunction
is_ordered(x)

Indicates whether the order of the elements of object x are important to its hashed value. If false, x's elements will first be collected and sort'ed before hashing them. When calling sort, hash_sort_by is passed as the by keyword argument. If x is a DictType, the elements are sorted by their keys rather than their elements.

source
StableHashTraits.hash_sort_byFunction
`hash_sort_by(x)`

Defines how the elements of a hashed container x are sorted if is_ordered of x returns false. The return value of this function is passed to sort as the by keyword.

source
StableHashTraits.parent_contextFunction
StableHashTraits.parent_context(context)

Return the parent context of the given context object. (See transformer and StableHashTraits.@context for details of using context).

This is normally all that you need to know to implement a new context. However, if your context is expected to be the root context—one that does not fallback to any parent (akin to HashVersion)—then there may be a bit more work involved. In this case, parent_context should return nothing.

source
StableHashTraits.@contextMacro
StableHashTraits.@context MyContext

Shorthand for declaring a hash context.

Contexts are used to customize the behavior of a hash for a type you don't own, by passing the context as the second argument to stable_hash, and specializing methods of transformer or StableHashTraits.transform_type on your context (see example below).

The clause @context MyContext is re-written to:

struct MyContext{T}
    parent::T
end
StableHashTraits.parent_context(x::MyContext) = x.parent

The parent context is typically another custom context, or the root context HashVersion{4}().

Example

StableHashTraits.@context NumberAbs
transformer(::Type{<:Number}, ::NumberAbs) = Transformer(abs; hoist_type=true)
stable_hash(10, NumberAbs(HashVersion{4}())) == stable_hash(-10, NumberAbs(HashVersion{4}()))

See Also

source
StableHashTraits.WithTypeNamesType
WithTypeNames(parent_context)

In this hash context, StableHashTraits.transform_type returns module_nameof_string for all types, in contrast to the default behavior (which mostly uses nameof_string(StructType(T))).

Unstable

module_nameof_string's return value can change with non-breaking changes if e.g. the module of a function or type is changed because it's considered an implementation detail of a package.

source
StableHashTraits.TablesEqType
TablesEq(parent_context)

In this hash context the type and structure of a table do not impact the hash that is created, only the set of columns (as determined by Tables.columns), and the hash of the individual columns matter.

source