Confused about global variables

Consider the following code

module T

const a = true

function c_a(new_a)
 global a
 a = new_a
end

function p_a()
 println(a)
end

end

However, T.p_a() does not seem to pick up the changed a after running T.c_a(false)

julia> VERSION
v"0.6.0"

julia> T.a
true

julia> T.p_a()
true

julia> T.c_a(false)
WARNING: redefining constant a
false

julia> T.a
false

julia> T.p_a()
true

What am I doing wrong?
(Just to be clear, I want to change the value of the global var a and use this in functions in the same module T)

Thanks

1 Like

See Strange behavior, when constant is redefined inside a function

13 Likes

I think the problem is that the “performance tips” section of the manual tells people to declare global variables as const or they will be slow. When people then discover that the value of a const can be changed, they figure that they’ve found a way to have fast globals.

Better documentation might help, but a much cooler solution would be if constants were treated as inlined functions, so that redefining a constant triggers re-compilation of every function where it is used. (I don’t care if this is slow. Just add another performance tip saying “avoid redefining constants”.)

2 Likes

Absolutely not.

The code below gives the expected behaviour.

A language I daren’t mention here calls variables with this behaviour ‘Private’, not ‘Global’.

For package quality code, this way of doing things is perhaps sub-optimal. But for everyday code, this is a great way of doing things.

module T

global a = true

function c_a(new_a)
 global a
 a = new_a
end

function p_a()
 println(a)
end

end
1 Like

What is a good use case for global variables? Is there an argument that can be made for their inevitability?
Or, can global variables be avoided altogether?

It is already there:

Note that const only affects the variable binding; the variable may be bound to a mutable object (such as an array), and that object may still be modified.

so eg

module T

const a = [true]
change_flag(flag::Bool) = a[1] = flag
get_flag() = a[1]

end

Functions are globals

Yes, but they are not, strictly speaking, global variables. They behave like something halfway between a variable and a const. Compare the following examples:

a = 0
function f()
    global a = 1
    println(a)
end

b() = 0
function g()
    global b() = 1
    println(b())
end

const c = 0
function h()
    global c = 1
    println(c)
end

The first invocation of f() prints 1, but the first invocations of g() and h() print 0. Still, g() prints 1 the second time, while h() prints 0 every time.

If it were up to me, I’d forbid redefining both functions and global consts from inside a function. If someone really wants to do it, he should be forced to use an eval to drop down to the global scope.

1 Like

No, they are even more restricted than global constants. What you are doing in your code is mutation and not changing the binding at all.

So the confusion for me was that I thought const fixes the type of the variable and that cannot be changed, but the value can be changed

Indeed T.a did change its value

But T.p_a() was compiled and cached and hence assumed T.a to not change value

Correct me if my understanding is wrong

For what it’s worth, using const global containers and treating them as you would any other normal container is something I occasionally resort to, and it always seems to work just fine (perhaps the devs will have further commentary on whether this is really a smart thing to do). In my experience, there are some (rare) instances when it is simply far more convenient to associate certain variables with a module than to force users to have to juggle objects which manage them. Otherwise, I’m not too sure why this issue keeps coming up: why would you assume that redefining something that you explicitly declared to be a const would result in “normal” behavior?

The value cannot be changed without a warning telling you that things shouldn’t crash but can start to be inconsistent. What inconsistency you might see is only an implementation detail.

If you really need something that doesn’t change type, use a global const Ref.

I think this is a great solution.