module Bar
x = 1
foo() = x
export foo
end
using .Bar
foo()
But this does not:
module Tofu
counter = 0
f() = (counter += 1; counter)
export f
end
using .Tofu
f()
The latter gives me a ‘counter not defined’ error.
I’m guessing this is an implementation thing. My guess is that the compiler binds 1 to f() in the first example so x is actually never referenced in the call to f(). But from a coding perspective this makes no sense since the first and second example should produce similar results from that perspective.
Is there a way to define a variable which is accessible within the module but not outside it except when it is explicitly referenced?
julia> module Bar
x = 1
foo() = x
x= 2
export foo
end
Main.Bar
julia> using .Bar
julia> foo()
2
I believe the issue is that the parentheses are creating a local context, so counter is expected to be local. With @GunnarFarneback’s fix the global keyword tells the compiler that counter is global. (It’s a subtly push to keep people from using global variables, as they don’t perform as well as local variables.)
I think you are just seeing the normal scope behaviour, but inside a module:
julia> x = 1
1
julia> function f()
x = x + 1
end
f (generic function with 1 method)
julia> f()
ERROR: UndefVarError: x not defined
Stacktrace:
[1] f() at ./REPL[2]:2
[2] top-level scope at REPL[3]:1
julia> function f()
x + 1
end
f (generic function with 1 method)
julia> f()
2
julia> function f()
global x = x + 1
end
f (generic function with 1 method)
julia> f()
2
Meaning: if you want to reassign the value of a global variable inside a function, you need to explicitly mention that it is global. (I had to think how phrase this, because the ambiguity exists only when one tries to use and assign the same label in the same expression. Otherwise one is just assigning a new label to a new value, or using the value of the global variable).
Where the behaviour is different in the REPL and in a module is with loops:
julia> module M
x = 1
for i in 1:3
x = x + 1
end
end
ERROR: UndefVarError: x not defined
Stacktrace:
[1] top-level scope at ./REPL[15]:4
julia> x = 1
1
julia> for i in 1:3
x = x + 1
end
julia> x
4
julia> module M
x = 1
for i in 1:3
global x = x + 1
end
end
WARNING: replacing module M.
Main.M
julia> M.x
4
Maybe when someone does that inside a module we should return the same good warning/error message that one gets when one tries to do that in a file?
julia> include("./mod.jl")
┌ Warning: Assignment to `x` in soft scope is ambiguous because a global variable by the same name exists: `x` will be treated as a new local. Disambiguate by using `local x` to suppress this warning or `global x` to assign to the existing global variable.
└ @ ~/Drive/Work/JuliaPlay/mod.jl:3
ERROR: LoadError: UndefVarError: x not defined
(not that I see many use cases for a loop that is executed on module loading)