In Package A we would like to offer facilities to write metadata about models of a specific type that would be written in other packages, say B.
To do this we have something like this
In Package A:
module A
input_type(T) = nothing
function meta(T, input_type)
ex = quote
input_type(::Type{<:$T}) = $input_type
end
eval(ex)
end
end
in Package B:
module B
import A: meta
export NewModel
struct NewModel end
meta(NewModel, Real)
end
The expected output being that
julia> using A, B
julia> input_type(B)
Real
(Incidentally if you do this in the REPL replacing import A with import Main.A it seems to work fine).
However in my context A and B are two separate packages and doing the steps above can lead to the following warning when precompiling B:
using B
[ Info: Precompiling B [...]
WARNING: eval into closed module A:
Expr(:block, #= Symbol( ...
resulting in a warning incremental compilation may be broken for this module. I suspect this is due to a poor use of eval and issues with where/when things are evaluated but it’s unclear to me how to fix the issue.
There’s no need to use eval() here: Julia’s normal method dispatch already handles this situation perfectly:
julia> module A
export input_type
input_type(T) = nothing
end
WARNING: replacing module A.
Main.A
julia> module B
import ..A: input_type
export NewModel
struct NewModel end
input_type(::Type{NewModel}) = Real
end
WARNING: replacing module B.
Main.B
julia> A.input_type(B.NewModel)
Real
Yes I’m aware this could be accomplished by multiple dispatch and I apologise that my example is a bit too simplistic and encourages this answer.
Let’s say I really want to use eval here because I don’t want to call input_type in module B (effectively there’s a series of such functions from A that I want to be wrapped in that function meta to reduce code duplication when possible).
Maybe to clarify, I’d like A to expose a function meta that can assign a number of traits, something like
function meta(T, ....)
trait_function1(T, ...)
trait_function2(T, ...)
end
If I do this without quote I get other errors like “static parameters cannot be used in closure” so this is why I went for eval.
I’m still pretty sure eval() is the wrong tool here. What about a macro that expands to the code you actually want to generate? This seems like a perfect use-case for a macro.
Yeah I guess I should try with a macro, I’ll report here if I manage to get that working, thanks. (and I agree that eval is likely not the best tool).
So if anyone should read this question in the future, I finally found out that the problem was to specify properly where the eval happens (and yes you actually have to use eval here because the emitted code has to be from A not B)
Anyway so calling parentmodule(T).eval solves the problem, it took me some time to find it but it works perfectly for my use case