I need to be able to programmatically define a new global variable in a module. Till Julia v1.11 I was able to do this as
function define_global()
m = Module(:MyModule)
Core.eval(m, :(global data))
setglobal!(m, :data, 123)
m
end
This, however, does not work anymore and raises an error:
julia> define_global()
ERROR: Global MyModule.data does not exist and cannot be assigned.
Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).
Hint: Declare it using `global data` inside `MyModule` before attempting assignment.
Stacktrace:
[1] define_global()
@ Main ./REPL[7]:4
[2] top-level scope
@ REPL[8]:1
This, presumably, is a consequence of the new world-age mechanism. In fact, the code below with invokelatest
solves the problem:
function define_global(name)
m = Module(:MyModule)
Core.eval(m, :(global data))
invokelatest(setglobal!, m, :data, 123)
m
end
Is there are nicer and cleaner way of doing what I am trying to do?
1 Like
Just to elaborate, I believe this is associated to the new world age mechanism, which in Julia 1.12 is also applied to global variables. This is creating several issues for me, since in my code I need to define and use, depending on userâs input, a new module together with new function definitions and global variables. I can still do this in 1.12, but only at the top-level in the REPL.
That workflow sounds kind of cursed to me. Could elaborate a bit what you want to achieve? Perhaps there is more idiomatic to your underlying issue.
2 Likes
I am doing Bayesian inference in the parameters of a fairly complex physical model. The model is specified by the user in a (YAML) configuration file. That file is loaded and translated into Julia code by a routine I wrote. The user-generate Julia code is all contained within a module, which includes all routines necessary to do inference (therefore to computer the likelihood, the prior and so on), together with user-set parameters and external data. The user-set parameters and external data are stored in the module as global variables.
Interesting stuff!
Am I guessing correctly that the yaml interface is due to external constraints? Otherwise a native Julia interface with some convenience macros woulde likely be a more streamlined experience - both for you and the users of your code.
Could you simply(?) wrap you generated code into a function and provide the parameters via function arguments? That also should be a lot faster since global variables are slow (especially if untyped). Perhaps youâd need to generate both a struct for holding parameters and some functions for computations.
Can I find your code that does this translation somewhere?
Btw: do you know Turing.jl? To my knowledge that does Bayesian inference and has a nice DSL for specifying models that then generates efficient Julia code.
1 Like
Thank you for your resposes @abraemer !
Yes, in a sense is due to âexternal constraintsâ: many users of the code are not experienced Julia programmers and it would be complicated for them to use the code properly.
The code is already largely wrapped within functions. In fact, it is composed of two main parts: a library, which is âstaticâ and does not depend on the user input, and a user interface, responsible of putting together the calls to the library depending on the user input. The latter is the problem of course, because it is at this level when I need to collect all the user input, find out the parameters to use for the inference and the constant quantities, and decide which functions are needed. At this stage I also take out of the inference constant quantities (i.e., I precompute them and store in an external constant structure passed to the likelihood).
Macros anyway have been my first attempt when I started this project a couple of years ago, but things with macros get very quickly quite confusing, especially if one needs to use macros within macros (which is something I had to do at a certain point).
However, following your kind suggestion, I will reconsider if there is a way to implement things with a macro.
I know Turing.jl and I have been trying to use for my purposes in the past, but with no success at all. It is certainly a very interesting project and I can see a lot of uses for it, but probably not for what I am trying to do (modeling of strong gravitational lenses), as the complexity of the systems and the number of free parameters is really large. For this reason it is really vital to have an highly optimized code (and yes, I do not use untyped global variables and all my critical functions do not have dynamic dispatch).
Thanks for your explanations and sorry for not having much of an answer so far. I guess I am still a bit confused we you need/use global variables and cannot pass the values via function arguments. To make this more explicit: You could generate a stuct that just includes every parameter you need and then pass that to every function. Then instead of accessing the global, you just access the structâs slot. I donât see why that should not be possible.
Also just in case it escaped your attention so far: there are so-called generated functions which fill a somewhat different niche than macros but can be very powerful for code generation. One way to implement a âcompiler for yamlsâ could be to parse the yaml and from the data build a type structure which you then feed into a generated function which constructs the actual code to be executed from the information in the type.
2 Likes