Question about functors and closures (I think 😅), with an example, but really a general Q

I think is enough with the next

next(r::MaskedRNG)                         = next(r.rng) & r.mask
next(r::Union{RNG, MaskedRNG}, m::Integer) = next(r) & m

Because the second next(r) will be dispatched correctly (without the m argument) to the first two.

Actually, I would go event further and write everything as follows, to avoid needing to write overly specific types.

mutable struct RNG <: AbstractRNG
    state::UInt64
end
# the default constructor promote to Integer by default

RNG() = RNG(time_ns())

struct MaskedRNG <: AbstractRNG
    rng::RNG
    mask::UInt64
end

MaskedRNG(nbits)       = MaskedRNG(RNG(), (1 << UInt8(nbits)) - 1)
# MaskedRNG(seed::Integer, nbits) = MaskedRNG(RNG(seed), (1 << UInt8(nbits)) - 1) # error when nbits = 64
# this next version allows setting high nbits using negatives, like -8 to mask to 0xff00000000000000
# in benchmarking it takes the same time as the above
MaskedRNG(seed::Integer, nbits) = MaskedRNG(RNG(seed), ~UInt64(0) >> ifelse(0 < abs(nbits) <= 64, sign(nbits)*64 - nbits, 64))

next(r::RNG)                               = (r.state = _rng(r.state))
next(r::MaskedRNG)                         = next(r.rng) & r.mask
next(r::Union{RNG, MaskedRNG}, m::Integer) = next(r) & m # this will dispatch correctly

function _rng(x::UInt64)
    x ⊻= (x <<  21)
    x ⊻= (x >>> 35)
    x ⊻= (x <<   4)
    return x % UInt64
end