It is often convenient to unpack some or all of the fields of a type, and pack, in the case of mutable datatypes (for immutables use Setfield.jl). This is often the case when a struct is passed into a function.
The @unpack
and @pack!
macros work to unpack
types, modules, and dictionaries (and can be customized for other
types too, see next section).
using UnPack
mutable struct Para
a::Float64
b::Int
end
function f!(var, pa::Para)
@unpack a, b = pa # equivalent to: a,b = pa.a,pa.b
out = var + a + b
b = 77
@pack! pa = b # equivalent to: pa.b = b
return out, pa
end
out, pa = f!(7, Para(1,2)) # -> 10.0, Para(1.0, 77)
Example with a dictionary:
d = Dict{Symbol,Any}(:a=>5.0, :b=>2, :c=>"Hi!")
@unpack a, c = d
a == 5.0 #true
c == "Hi!" #true
d = Dict{String,Any}()
@pack! d = a, c
d # -> Dict{String,Any}("a"=>5.0,"c"=>"Hi!")
@unpack
and @pack!
What happens during the (un-)packing of a particular datatype is
determined by the functions UnPack.unpack
and UnPack.pack!
.
The UnPack.unpack
function is invoked to unpack one entity of some
DataType
and has signature:
unpack(dt::Any, ::Val{property}) -> value of property
Note that unpack
(and pack!
) works with Base.getproperty
. By
default this means that all the fields of a type are unpacked but if
getproperty
is overloaded, then it will unpack accordingly.
Three method definitions are included in the package to unpack a composite type/module/NamedTuple, or a dictionary with Symbol or string keys:
@inline unpack(x, ::Val{f}) where {f} = getproperty(x, f)
@inline unpack(x::AbstractDict{Symbol}, ::Val{k}) where {k} = x[k]
@inline unpack(x::AbstractDict{<:AbstractString}, ::Val{k}) where {k} = x[string(k)]
The UnPack.pack!
function is invoked to pack one entity into some
DataType
and has signature:
pack!(dt::Any, ::Val{field}, value) -> value
Three definitions are included in the package to pack into a mutable composite type or into a dictionary with Symbol or string keys:
@inline pack!(x, ::Val{f}, val) where {f} = setproperty!(x, f, val)
@inline pack!(x::AbstractDict{Symbol}, ::Val{k}, val) where {k} = x[k]=val
@inline pack!(x::AbstractDict{<:AbstractString}, ::Val{k}, val) where {k} = x[string(k)]=val
More methods can be added to unpack
and pack!
to allow for
specialized unpacking/packing of datatypes. Here is a MWE of customizing
unpack
, so that it multiplies the values by 2:
using UnPack
struct Foo
a
b
end
p = Foo(1, 2)
@unpack a, b = p
a, b # gives (1, 2)
# Now we specialize unpack for our custom type, `Foo`
@inline UnPack.unpack(x::Foo, ::Val{f}) where {f} = 2 * getproperty(x, f)
@unpack a, b = p
a, b # now gives (2, 4)