Provide hooks to massage data after creation
Opened this issue · 12 comments
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?