I cannot import `global variable::Type`

Hi there,

I am using submodules and am surprised that I cannot import a globally defined config value which is not yet initialized.

While julia has a special syntax for such undefined globals

global variable::Type

I am unable import this undefined variable inside a submodule using TopLevelProject: variable. I get an error

ERROR: LoadError: UndefVarError: `variable` not defined

I guess the workaround arround is to use a const Ref, still I am very surprised that such typed globals cannot replace const Ref.

I think you should get the same error within the defining module if you haven’t assigned a value to it yet (like regularly trying to access the varable).
To avoid this, you check with @isdefined first or assign a sentinel value initially.

Another way would be to define

variable() = TopLevelProject.variable

which should also avoid the issue.

I am only using this inside Functions, so there is no need to really access the variable in precompile/macroexpand time.

I am not sure I follow - do my suggestions solve your problem?

I think the misconception may arise from what a variable in Julia is, conceptually:

julia> global g

julia> @isdefined g
false

julia> g = 2
2

julia> @isdefined g
true

In Julia, variables are bindings and to create a binding, there needs to be an object to create a binding to.

Whereas, for example in C, variables can be seen as boxes, so if you do declare a variable

int i;

then you already get the box - you just don’t know what’s in it, meaning i has the value of whatever bits were at this place in memory before.

To achieve the behaviour you want in Julia, you could:

  1. Just use a Ref - like you suggested.

  2. Depending on what Type actually is, choose a “placeholder” value that you can assign immediately.

  3. Only refer to the variable once it actually has a value (which you can check via @isdefined).

I think a const Ref is probably the way you want to go if you cannot initialize the global for some reason. With a Ref you can at least have an #undef.

What kind of Type were you considering this for?

1 Like

I’m a little confused: what would you do after you’ve imported the undefined global? The only thing you could theoretically do is assign to it. But Julia doesn’t let you do that, either:

julia> module M
           global var = 1
           export var
       end
Main.M

julia> using .M: var

julia> var = 2
ERROR: cannot assign a value to imported variable Main.var

You’ve gotta explicitly fully-qualify the thing:

julia> M.var = 2
2

And at that point, it also works if it’s not defined!

julia> module M2
           global var2
           export var2
       end
Main.M2

julia> using .M2

julia> var2
ERROR: UndefVarError: `var2` not defined

julia> M2.var2 = 3
3

julia> var2
3

I believe the idea here is that the imported variable is used inside functions, which will only be called once the variable is assigned a value.

As you’ve pointed out, one solution would be to not import the variable, and use the module-qualified name for it in the function. A Ref is another viable option here.

The best one is probably to restructure things so that this isn’t necessary. It’s usually possible to do that.

3 Likes

Or do foo = Foo.foo in the function, so that you only have to qualify it once. (Essentially a “local import”)

2 Likes

While often you don’t need to initialize variables on runtime, often you do need to do so.

In my case, which also answers your question @mkitti, I am using a global Configurations.jl @option struct to gather all my variables. Concretely there are some secrets which I need to initialize. I don’t want to write the secrets into the code, so this a classical example where you need runtime initialization of the concrete Configuration object.

Thank you for the workarounds! It is good to know that referencing it via the module name actually works.

For my intuition it is a bug that referencing it directly does not work.
Does someone know about Julia’s variable importing system whether this should work?

2 Likes