Why do function variables behave differently than global script variables?

Hello,

I came across something rather interesting today and was curious if someone could shed some light as to what the difference is. I’ve been primarily using Jupyter Lab and haven’t come across this issue before until now when I started a new script today. I’m finding that:

a = 0
for i=1:3 
    a += 1
end
println(a)

will fail, whereas

a = 0
for i=1:3 
    global a += 1
end
println(a)

and

function b()
    a = 0
    for i=1:3 
        a += 1
    end
    println(a)
end
b()

both work as expected. Why is there this difference? I have looked at this thread. For simple scripts, I guess I’m not sure why I need to make this distinction- part of the beauty of Julia for me is that speed can be optional, when convenience is nice. In this example, it’s not a big deal.

Thanks for your insight in advance.

1 Like

citing the official julia docs, in performace tips:

A global variable might have its value, and therefore its type, change at any point. This makes it difficult for the compiler to optimize code using global variables. Variables should be local, or passed as arguments to functions, whenever possible.

in the second case, the function has to call a reference to a. if that reference is a int, it will perform integer adition. if not, Julia will promote the result type to the adecuate one.
In the last case. the compiler already knows that a is a integer and it will not change, and even optimizes the whole loop away, for example, without the println:

julia> function bbb()
           a = 0
           for i=1:3
               a += 1
           end
       return a
       end
bbb (generic function with 1 method)

@code_llvm bbb()

;  @ REPL[232]:2 within `bbb'
; Function Attrs: uwtable
define i64 @julia_bbb_20195() #0 {
top:
;  @ REPL[232]:6 within `bbb'
  ret i64 3
}
4 Likes

In your first example, a inside and outside the loop are different variables; and the a outside is a global variable (i.e. defined in global scope, i.e. to top level of a module or at >> in the REPL). The rule is:


    an assignment would result in a modified global variable, or
    a variable is specifically marked with the keyword local.

Thus global variables are only inherited for reading, not for writing

https://docs.julialang.org/en/v1/manual/variables-and-scoping/#Local-Scope-1

Thus because the assignment would “result in a modified global variable” a new local variable is introduced. And because a += 1 also accesses this new a before its assignment it errors.

Example 2 works because you specifically tell Julia to use the global a.

Example 3 works because the outer a is not a global but also local. Then above rules don’t apply and a can be written to.

BTW, this was quite a controversial change late towards stabilizing Julia 1.0, see Another possible solution to the global scope debacle. Also, note that Jupyter notebooks handle this differently, there your example 1 works due to using SoftGlobalScope.jl.

4 Likes