mauro3 / Parameters.jl

Types with default field values, keyword constructors and (un-)pack macros
Other
419 stars 31 forks source link

Cannot construct parameters object when using `@cfunction` #126

Closed rvignolo closed 4 years ago

rvignolo commented 4 years ago

Hi Mauro!

As we know, Parameters.jl it can handle complex examples:

julia> nt2 = @with_kw (a = 1, f = t -> a + t, g = t -> f(t) + sin(t))
##NamedTuple_kw#255 (generic function with 2 methods)

julia> nt2().g(1.)
2.8414709848078967

This is nice. What is happening in the background is that the @with_kw macro produces a constructor function that receives named arguments so you can use them to define other parameters. This can be inspected using @macroexpand or @expand (from MacroTools.jl).

Now I want to make use of these features in a more complicated example. Let's say I would like to include the numerical derivative of f in my parameters by using GSL.jl package (I have already tested other packages but this one seems to be the fastest at the moment). The first thing I have to do is define a gsl_function object:

julia> using GSL

julia> const h = 9.765625e-4
0.0009765625

julia> const abserr = Cdouble[0]
const 1-element Array{Float64,1}:
 0.0

julia> const result = Cdouble[0]
1-element Array{Float64,1}:
 0.0

julia> nt2 = @with_kw (a = 1, f = t -> a + t, f′ = t -> deriv_central(@gsl_function(f), t, h, result, abserr))
##NamedTuple_kw#262 (generic function with 2 methods)

julia> nt2()
(a = 1, f = var"#80#84"{Int64}(1), f′ = var"#81#85"())

julia> nt2().f′(0.1)
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] (::var"#88#89")(::Float64, ::Ptr{Nothing}) at /Users/ramirovignolo/.julia/packages/GSL/yvp3l/src/manual_wrappers.jl:45
 [2] deriv_central(::gsl_function, ::Float64, ::Float64, ::Array{Float64,1}, ::Array{Float64,1}) at /Users/ramirovignolo/.julia/packages/GSL/yvp3l/src/gen/direct_wrappers/gsl_deriv_h.jl:36
 [3] (::var"#81#85")(::Float64) at ./none:1
 [4] top-level scope at none:1

It seems that @gsl_function expands and uses @cfunction (from Base). When this macro is expanded, f is not captured.

Does anyone know how to avoid this problem at all?

PD: I have already discussed this issue with Chris and we could not figure out how to solve it but he pointed out that I should reach you!

Thanks in advance!

mauro3 commented 4 years ago

Can you make an MWE without the GSL dependency, i.e. using @cfunction directly?

rvignolo commented 4 years ago

Hi,

I realized that I can remove the @with_kw macro to make a better MWE:

function testing()

  a = 1
  g = t -> a + t

  h = 9.765625e-4
  result = Cdouble[0]
  abserr = Cdouble[0]

  g′ = t -> deriv_forward(@gsl_function(g), t, h, result, abserr)

  g′(1.)

  return result[], abserr[]
end

Which returns:

julia> testing()
ERROR: UndefVarError: g not defined
Stacktrace:
 [1] (::var"#112#113")(::Float64, ::Ptr{Nothing}) at /Users/ramirovignolo/.julia/packages/GSL/yvp3l/src/manual_wrappers.jl:45
 [2] deriv_forward(::gsl_function, ::Float64, ::Float64, ::Array{Float64,1}, ::Array{Float64,1}) at /Users/ramirovignolo/.julia/packages/GSL/yvp3l/src/gen/direct_wrappers/gsl_deriv_h.jl:93
 [3] #109 at /Users/ramirovignolo/codigos/finance/UniversalMonteCarlo/examples/interestrate/shortrate/multifactor/FongVasicek_revisited.jl:328 [inlined]
 [4] testing() at /Users/ramirovignolo/codigos/finance/UniversalMonteCarlo/examples/interestrate/shortrate/multifactor/FongVasicek_revisited.jl:330
 [5] top-level scope at none:1

Also, I can remove the @gsl_macro and replace it:

function testing2()

  a = 1
  g = t -> a + t

  h = 9.765625e-4
  result = Cdouble[0]
  abserr = Cdouble[0]

  # g′ = t -> deriv_forward(@gsl_function(g), t, h, result, abserr)
  g′ = t -> deriv_forward(gsl_function(@cfunction((x, p) -> g(x), Cdouble, (Cdouble, Ptr{Cvoid})), 0), t, h, result, abserr)

  g′(1.)

  return result[], abserr[]
end

Which returns the same error.

So the problem is not related to @with_kw. For some reason, @cfunction cannot capture a runtime closure.

mauro3 commented 4 years ago

I have no idea here. I think you should post this to discourse. I'll close this. I hope this is ok, otherwise re-open.