Skip to content

Commit 62ed571

Browse files
committed
Allow reconstruct to construct different type (but compatible)
1 parent ecbf8df commit 62ed571

File tree

2 files changed

+63
-22
lines changed

2 files changed

+63
-22
lines changed

src/Parameters.jl

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,14 @@ function type2dict(dt)
198198
end
199199

200200
"""
201+
reconstruct(pp; kws...
202+
reconstruct(T::Type, pp; kws...)
203+
201204
Make a new instance of a type with the same values as the input type
202-
except for the fields given in the AbstractDict second argument or as
203-
keywords. Works for types, Dicts, and NamedTuples.
205+
except for the fields given in the keyword args. Works for types, Dicts,
206+
and NamedTuples. Can also reconstruct to another type, which is probably
207+
mostly useful for parameterised types where the parameter changes on
208+
reconstruction.
204209
205210
Note: this is not very performant. Check Setfield.jl for a faster &
206211
nicer implementation.
@@ -213,34 +218,56 @@ julia> struct A
213218
b
214219
end
215220
216-
julia> a = A(3,4)
221+
julia> x = A(3,4)
217222
A(3, 4)
218223
219-
julia> b = reconstruct(a, b=99)
224+
julia> reconstruct(x, b=99)
220225
A(3, 99)
226+
227+
julia> struct B{T}
228+
a::T
229+
b
230+
end
231+
232+
julia> y = B(sin, 1)
233+
B{typeof(sin)}(sin, 1)
234+
235+
julia> reconstruct(B, y, a=cos) # note reconstruct(y, a=cos) errors!
236+
B{typeof(cos)}(cos, 1)
221237
```
222238
"""
223-
function reconstruct(pp::T, di) where T
224-
if pp isa AbstractDict
225-
pp = deepcopy(pp)
226-
for (k,v) in di
227-
!haskey(pp, k) && error("Field $k not in type $T")
228-
pp[k] = v
239+
reconstruct(pp::T, di) where T = reconstruct(T, pp, di)
240+
reconstruct(pp; kws...) = reconstruct(pp, kws)
241+
reconstruct(T::Type, pp; kws...) = reconstruct(T, pp, kws)
242+
function reconstruct(::Type{T}, pp, di) where T
243+
di = !isa(di, AbstractDict) ? Dict(di) : copy(di)
244+
ns = if T<:AbstractDict
245+
if pp isa AbstractDict
246+
keys(pp)
247+
else
248+
fieldnames(typeof(pp))
229249
end
230-
return pp
231250
else
232-
di = !isa(di, AbstractDict) ? Dict(di) : copy(di)
233-
ns = fieldnames(T)
234-
args = []
235-
for (i,n) in enumerate(ns)
251+
fieldnames(T)
252+
end
253+
args = []
254+
for (i,n) in enumerate(ns)
255+
if pp isa AbstractDict
256+
push!(args, pop!(di, n, pp[n]))
257+
else
236258
push!(args, pop!(di, n, getfield(pp, n)))
237259
end
238-
length(di)!=0 && error("Fields $(keys(di)) not in type $T")
239-
return pp isa NamedTuple ? T(Tuple(args)) : T(args...)
240260
end
241-
end
242-
reconstruct(pp; kws...) = reconstruct(pp, kws)
261+
length(di)!=0 && error("Fields $(keys(di)) not in type $T")
243262

263+
if T<:AbstractDict
264+
return T(zip(ns,args))
265+
elseif T <: NamedTuple
266+
return T(Tuple(args))
267+
else
268+
return T(args...)
269+
end
270+
end
244271

245272
###########################
246273
# Keyword constructors with @with_kw

test/runtests.jl

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using Parameters, Test, Markdown, REPL
22

3-
# misc
4-
a8679 = @eval (a=1, b=2)
5-
ra8679 = @eval (a=1, b=44)
3+
# reconstruct
4+
a8679 = (a=1, b=2)
5+
ra8679 = (a=1, b=44)
66
@test ra8679 == reconstruct(a8679, b=44)
77
@test_throws ErrorException reconstruct(a8679, c=44)
88

@@ -19,6 +19,20 @@ a8679 = A8679(1, 2)
1919
@test A8679(1, 44) == reconstruct(a8679, b=44)
2020
@test_throws ErrorException reconstruct(a8679, c=44)
2121

22+
@test reconstruct(A8679, (a=1, b=2), b=44) == A8679(1, 44)
23+
@test reconstruct(A8679, Dict(:a=>1, :b=>2), b=44) == A8679(1, 44)
24+
@test_throws KeyError reconstruct(A8679, Dict("a"=>1, "b"=>2), b=44) == A8679(1, 44)
25+
@test reconstruct(typeof((a=1, b=2)), A8679(1, 2), b=44) == (a=1, b=44)
26+
@test reconstruct(Dict{Symbol,Any}, A8679(1, 2), b=44) == Dict(:a=>1, :b=>44)
27+
28+
struct B8679{T}
29+
a::T
30+
b
31+
end
32+
a8679 = B8679(sin, 1)
33+
@test reconstruct(B8679, a8679, a=cos) == B8679{typeof(cos)}(cos, 1)
34+
@test_throws MethodError reconstruct(a8679, a=cos)
35+
2236
##########
2337
# @with_kw
2438
##########

0 commit comments

Comments
 (0)