Parameters.jl icon indicating copy to clipboard operation
Parameters.jl copied to clipboard

Provide hooks to massage data after creation

Open djsegal opened this issue 7 years ago • 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?

djsegal avatar Apr 21 '18 22:04 djsegal

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 avatar Apr 22 '18 06:04 mauro3

@mauro3 MWE would be a way to add finalizers to constructed mutable structs

TsurHerman avatar Apr 24 '18 09:04 TsurHerman

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.

mauro3 avatar Apr 25 '18 09:04 mauro3

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.

TsurHerman avatar Apr 25 '18 10:04 TsurHerman

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.

mauro3 avatar Apr 25 '18 20:04 mauro3

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:

djsegal avatar Aug 16 '18 20:08 djsegal

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?

mauro3 avatar Aug 20 '18 07:08 mauro3

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...

mauro3 avatar Aug 20 '18 07:08 mauro3

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

mauro3 avatar Jan 25 '19 10:01 mauro3

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

djsegal avatar Mar 25 '19 22:03 djsegal

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?

mauro3 avatar Mar 25 '19 22:03 mauro3

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?

mauro3 avatar Nov 23 '19 23:11 mauro3