Sometimes defining a new struct is accompanied by a bit of boilerplate. For instance the default definition of Base.hash is by object id. Often a hash that is based on the object structure is preferable however. Similar for ==.
StructHelpers aims to simplify the boilerplate required for such common tweaks.
struct S
a
b
end
@batteries S
@batteries S hash=false # don't overload `Base.hash`
@batteries S kwconstructor=true # add a keyword constructor
@batteries S kwconstructor # bare-symbol shorthand for `kwconstructor=true`If you want only a hand-picked subset of batteries (no defaults), use
@battery:
@battery S kwconstructor # only the keyword constructor
@battery S eq isequal hash # only structural ==, isequal, hashTo share a configuration across many structs, pass a NamedTuple:
const config = (kwshow=true, kwconstructor=true, eq=false)
@batteries S1 config
@batteries S2 config typesalt=0xdeadbeef # config + per-struct override
@battery S3 config # opt-in form, same configA config only overrides the keys it mentions. With @batteries, every
key the config doesn't set keeps its default value (e.g. S1 above
still gets the default structural isequal, hash, selfconstructor
etc.). With @battery the same config picks exactly the listed
batteries and nothing else, since @battery's baseline has every flag
turned off.
Useful configs could look like this:
# Value-like records: nice display + keyword construction, structural
# `==` / `isequal` / `hash` from the defaults.
const value_like = (kwshow=true, kwconstructor=true)
# Plain data you also want to (de)serialize via StructTypes.jl / JSON3.
const serializable = (kwshow=true, kwconstructor=true, StructTypes=true)
# Identity semantics: keep ergonomic show/construction, but opt out of
# structural equality and hashing (e.g. for mutable handles or types
# whose fields aren't meaningfully comparable).
const identity_like = (kwshow=true, kwconstructor=true,
eq=false, isequal=false, hash=false)
# Reproducible cross-machine hashes: combine with a per-struct typesalt.
const stable_hash = (kwshow=true, kwconstructor=true)
@batteries Point value_like
@batteries Config serializable
@batteries FileHandle identity_like
@batteries CacheKey stable_hash typesalt=0xdeadbeefcafebabeFor all supported options and defaults, consult the docstring:
julia>?@batteriesTwo options overload Base.show; pick at most one (passing both is an
error):
-
kwshow=truealways rendersT(f1 = v1, f2 = v2, …)— every field, named, in declaration order. The output shape is fixed and stable across versions, which makes it well-suited to diagnostics and golden-file tests. It round-trips throughevalonly when the type has a keyword constructor (e.g. viakwconstructor=trueorBase.@kwdef). -
showrepr=trueprints a heuristically short constructor call that recreates the object. It probes every constructor of the type (positional, keyword, hybrid), omits trailing fields that already match a default, and substitutes shorter literals where the constructor still accepts them (e.g.0x2a→42,2//1→2, uniform vectors →fill(v, n)). Because the result depends on the field values and on the package's heuristics, the exact output is not guaranteed to be stable across minor releases — don't pin golden files against it. If no constructor recreates the object,showreprfalls back to a non-executableT(field = value, …)rendering.
Rule of thumb: pick kwshow if you want a predictable, name-every-field
diagnostic; pick showrepr if you want Base.show to produce an
idiomatic, recreatable, as-short-as-reasonable form for end users.
- AutoHashEquals requires annotating the struct definition. This can be inconvenient if you want to annotate the definition with another macro as well.
- StructEquality is similar to this package.