Hi, is there an easy way to have Parameters.jl
skip missing keys or fields?
using Parameters
mydict = Dict(a=>3,b=>2.3,c=>"mystr")
@unpack (a,b,c,d) = mydict
ERROR: KeyError: key "d" not found
I would like it to just skip over the missing key. The reason I want this behavior is I am carrying around two lists which hold fixed parameters and ones to be found via optimization. It would be nice if I could just unpack them, without having to check which ones were in which list like:
@unpack (fixed1,fixed2,...) = fixedParams
@unpack (fitted1,fitted2,...) fittedParams
and instead being able to just say
@unpack (p1,p2,...) = fixedParams
@unpack (p1,p2,...) = fittedParams
I don’t think so. Part of the reason @unpack
is fast is because it doesn’t have these kinds of checks. A macro doesn’t know what’s inside a Dict
.
1 Like
Is there a way to write my own method then that does this check? It doesn’t need to be fast.
UnPack.jl is actually super simple once you understand metaprogramming.
https://github.com/mauro3/UnPack.jl/blob/master/src/UnPack.jl
In particular, lines 32-36 are what you would want to change. Instead of getindex
you would want get(d, x, nothing)
or similar.
3 Likes
I tried adding both of the following methods:
@inline unpack(x::AbstractDict{<:AbstractString}, k) = get(x,string(k),nothing)
@inline unpack(x::AbstractDict{<:AbstractString}, ::Val{k}) where {k} = get(x,string(k),nothing)
but still got the same error:
test=Dict("a"=>1,"b"=>2,"c"=>"mystr")
@unpack (a,b,c,d) = test
ERROR: KeyError: key "d" not found
Could you please tell us more about what steps you did to add the methods? Did you add the package for development using ] dev
?
Ah sorry. I thought I was able to add methods similar to how I can add methods in Base.jl
. So I was just adding them inline in my Julia script.
You can, but you should import
methods first.
But this is type piracy and shouldn’t be used if you are writing a library (but more or less fine in your local script)
Here is an MWE
julia> using UnPack
julia> import UnPack: unpack
help?> unpack
search: unpack UnPack @unpack
This function is invoked to unpack one field/entry of some DataType dt and has signature:
unpack(dt::Any, ::Val{sym}) -> value of sym
The sym is the symbol of the assigned variable.
Three definitions are included in the package to unpack a composite type 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{S}, ::Val{k}) where {S<:AbstractString,k} = x[string(k)]
More methods can be added to allow for specialized unpacking of other datatypes.
See also pack!.
julia> unpack(x::AbstractDict{S}, ::Val{k}) where {S<:AbstractString,k} = get(x, string(k), nothing)
unpack (generic function with 3 methods)
julia> d = Dict(["a" => 1])
Dict{String, Int64} with 1 entry:
"a" => 1
julia> @unpack a, c = d
Dict{String, Int64} with 1 entry:
"a" => 1
julia> c
That said, don’t do this… Maybe you can define your own macro, use @unpack
’s parsing, but call your own function instead of unpack
.
2 Likes
So how should one do this properly?
Fork @unpack
and change the name of the macro?
I would like to use it as a convenience function in my own code. I mean, I still want to use the default Parameters.jl
.
When I say “improper” it means that if you want
@unpack a = d
to not fail when a
is not in d
, you need to commit type piracy, which means overwriting a method you don’t own for a type you don’t own.
You can do that in your own code (see above), but the consequence is
- When people read your code, the behavior of
@unpack
will be different than they expect
- If other packages use your code in any way, existing uses of
@unpack
will behave differently than the original authors expected.
So you can’t be “correct” and change the behavior of @unpack
.
1 Like