Writing a new modifier
A modifier is a wrapper type around exactly one univariate distribution that changes one behaviour and delegates the rest. This page describes the pattern the built-in modifiers follow, using them as worked examples.
The shape of a modifier
A wrapper struct subtyping
UnivariateDistribution, holding the inner distribution plus the modifier's parameters. Validate parameters in an inner constructor — seeAffine's positive-scale check insrc/Affine.jl.A lowercase constructor verb (
affine,weight,thin,modify) as the user-facing entry point. If an "absent" parameter is meaningful, add a::Nothingpassthrough method returning the distribution unchanged, asweight(d, nothing)andthin(d, nothing)do.A
get_distmethod exposing the inner distribution, one line:
get_dist(d::MyWrapper) = d.distThis joins your wrapper to the unwrap protocol, so get_dist_recursive and downstream tooling work without knowing your type.
Delegation
Most methods should delegate to the inner distribution. For bulk delegation, Transformed uses an @eval loop worth copying:
for f in (:minimum, :maximum, :mean, :var, :std, :mode, :median,
:skewness, :kurtosis, :entropy)
@eval Distributions.$f(d::MyWrapper) = Distributions.$f(d.dist)
endThen override only the methods your modifier actually changes (logpdf for a weight, the full change-of-variables set for an affine map). Remember Base.eltype — without it, batch sampling rand(rng, d, n) falls back to Vector{Any} and errors.
Forward-transform ops
A new forward op (alongside ThinOp/CumulativeOp) does not need a new wrapper type. Implement _apply_op(op, series) and, if the op has a displayable parameter, _op_params(op); then construct it with series_transform(d, op).
AD friendliness
Keep branches data-driven (on types and object identity), never on sampled parameter values, so gradients survive all backends. Add a gradient scenario for your modifier to test/ADFixtures/src/ADFixtures.jl, mirroring the existing entries — the AD CI sweeps it across ForwardDiff, ReverseDiff, Enzyme and Mooncake automatically.
Checklist
[ ] Struct + validated inner constructor
[ ] Constructor verb (+
Nothingpassthrough if applicable)[ ]
get_distmethod[ ] Delegations + overridden behaviour
[ ]
Base.eltype[ ] Docstrings in the house style (
@docblocks,# Examples,# See also)[ ] Export the verb; mark the type
publicinsrc/public.jl[ ] Tests in
test/MyWrapper.jl+ an ADFixtures scenario