minimum implementation for type wrapping a NamedTuple
I have a type wrapping a NamedTuple, that essentially forwards getproperty. For the purposes of the MWE, let it be
struct WrapNT{NT<:NamedTuple}
nt::NT
count::Int # for the purposes of the MWE, maintain as length(nt)
end
@inline _nt(wnt::WrapNT) = getfield(wnt, :nt) # save typing
Base.show(io::IO, wnt::WrapNT) = print(getfield(wnt, :count), " fields ", _nt(wnt))
# constructor
wrap_NT(nt::NamedTuple) = WrapNT(nt, length(nt))
Base.propertynames(wnt::WrapNT) = propertynames(_nt(wnt))
Base.getproperty(wnt::WrapNT, sym::Symbol) = getproperty(_nt(wnt), sym)
The question is: how should I hook into the API of Accessors.jl so that stuff "just works"? I experimented and found that
function Accessors.insert(wnt::WrapNT, lens::PropertyLens{S}, val) where S
wrap_NT(Accessors.insert(_nt(wnt), lens, val))
end
function Accessors.set(wnt::WrapNT, lens::PropertyLens{S}, val) where S
wrap_NT(Accessors.set(_nt(wnt), lens, val))
end
function Accessors.delete(wnt::WrapNT, lens::PropertyLens{S}) where S
wrap_NT(Accessors.delete(_nt(wnt), lens))
end
works, but from the manual it is not clear if this is enough.
The most convenient way is to define a setter for your _nt function first, and use it to forward set/insert/delete:
Accessors.set(wnt::WrapNT, ::typeof(_nt), nt) = wrap_NT(nt)
Accessors.set(wnt::WrapNT, lens::PropertyLens, val) = set(wnt, lens ∘ _nt, val)
Accessors.insert(wnt::WrapNT, lens::PropertyLens, val) = insert(wnt, lens ∘ _nt, val)
Accessors.delete(wnt::WrapNT, lens::PropertyLens) = delete(wnt, lens ∘ _nt)
Thanks! I am assuming you meant delete without the val argument.
This works just great. If you think the manual would benefit from a PR, I would be happy to make one using this example.
It would be cool to have something like MacroTools.@forward for writing these methods automatically!
Indeed! We already have a convenience tool for that, but it doesn't apply to PropertyLens specifically:
@accessor myfunc(x) = x.myfield
enables set/insert/delete on myfunc.
Maybe,
@accessor Base.getproperty(...) = ...
should have special handling and forward ::PropertyLens?..