Parameters.jl
Parameters.jl copied to clipboard
Provide hooks to massage data after creation
If you have mutable structs, you can have fields you want to set before or after the constructor Parameters.jl uses.
Is there a way to provide access to wrapping that function?
Maybe with @before_create and @after_create (hook) macros?
I don't quite understand. Would it be solved with "Calculated fields" #18?
Also note that if you specify the positional constructor (for all fields) then the kw-constructur will run through that. That may allow to manually program what you're after.
@mauro3 MWE would be a way to add finalizers to constructed mutable structs
But can't you provide finalizers with just registering a function: https://docs.julialang.org/en/stable/stdlib/base/#Base.finalizer: finalizer(::MyStruct, finalfn)? I don't see how adding macro magic to that would make it more concise or more readable.
When do you add a finalizer? Usually at object creation time. Otherwise I have to manually add a finalizer every time I construct a new object.
Thanks, now I get it. I think the post-create hook could be ok to implement as that would be a hook of a function of one argument. The pre-create hook however (I think) would need to be a function which has all fields as positional arguments, so it would not be any easier to write than the positional constructor.
So, for the post-create hook
@macroexpand @with_kw struct A
a=1
b=2
@post_create a -> finalizer(a, finalfn) # post_create_fn = a -> finalizer(a, finalfn)
end
struct A
a
b
A(; a=1, b=2) = begin
A(a, b)
end
A(a, b) = begin
post_create_fn(new(a, b))
end
end
I never had an application for this so it's low on my priority list. But a PR, if it does not add too many LOCs, would be welcome.
Current workaround is making data structure have one not-parameterized field and then using a base constructor that accepts at least one argument (i.e. the non-parameterized one)
This is related to the following line of code:
I don't understand that work-around, maybe you can post a MWE. What do you mean with "base constructor"? An inner or outer constructor?
If the pre-create hook would be just a block of code to be inserted into the positional inner constructor, then it would have access to all variables. Something like:
@macroexpand @with_kw struct A
a=1
b=2
@pre_create begin
println("Before `new`")
a = a+1
end
@post_create a -> finalizer(a, finalfn) # post_create_fn = a -> finalizer(a, finalfn)
end
struct A
a
b
A(; a=1, b=2) = begin
A(a, b)
end
A(a, b) = begin
begin
println("Before `new`")
a = a+1
end
post_create_fn(new(a, b))
end
end
Unfortunately, this makes the pre and post hooks asymmetric...
Another use case from #72 is to add a deprecation warning to a field:
struct AB
a
b
end
function AB(a,b)
depwarn("b is deprecated", :AB)
return AB(a)
end
I don't think having symmetric pre/post hooks is that important?
One ultimate use case might be something like:
@with_kw struct Wire
...
eta_cu = 1424
eta_ss = 137
w = 15
...
dr = h_C
N = round( ( r_2 - r_1 ) / dr - 1 )
s_0 = 0.5 * ( r_1 + r_2 )
...
function f_s(a,s)
...
end
function eta_para(J_para,a,s)
...
end
...
end
I'm a bit confused by the question you ask yourself.
The example you give seems closer to calculated fields, no? Or what is a and s?
This would also be the way to generalize what macros (and other code) do inside the type-def. Currently @assert is special cased, such that it gets added to the constructor. Could there be a general way to do this?