Seek for a detailed description on scopes and modules

I am an R / Fortran programmer trying to understand scopes in Julia. I know the great debate local/global, but what I’m looking for is a document (resource) where the actual rules for scopes are described with examples. Also for modules that use modules. Is there any such document? The official docs at Julia Documentation · The Julia Language are complete but rather terse. Thanks.

Not sure if you missed it or just had further questions, but scope is documented in the manual here: https://docs.julialang.org/en/stable/manual/variables-and-scoping/#scope-of-variables-1. If you scroll down, there are some examples included.

The constructs let, for, while, try, catch, comprehensions, function and any syntax for a function (lambdas, do blocks) introduce new scopes.

When you see x = in a scope body, then it does one of two things:

  1. If there is already an outer local variable called x, it assigns to that;
  2. Otherwise it declares a new x local to this new scope and assigns to it.

It does not matter if there is an outer global named x or not: it will never be assigned unless you write global x to declare that you want x to refer to the global named x.

We can crowd-source some examples.

1 Like

so you have global and a telescopic hierarchy of local ?

just a minimal example that confuses me. How does the function know that there is a Dict called tablefact ?

function ffact(i)
    tablefact[i]=i*tablefact[i-1]
    return tablefact[i]
end

global tablefact=Dict{Int64,Float64}()
tablefact[0]=1
for j in 1:10
    println(ffact(j))
end

println(tablefact)

Yes, there is a global scope in each module and a hierarchy of local scopes introduced by each scope-introducing construct.

You never assign to tablefact in the ffact body so it must refer to a global.

2 Likes

you mean, I never assign to the whole tablefact Dict ? Only to one of its cells?

Yes, that is not assignment, it is mutation of the Dict object, which is very different:

2 Likes

I’d like to expand on the second point: Declaring a local x is not an instruction, it is a syntactic annotation of the entire scope. Whether an x is local to the scope is independent of what your code does, and can be decided from the syntax tree alone: If x ever appears on the left-hand side of an assignment (x = ... or x += ..., not x.foo = ... or x[i] = ... or x .= ...), and there is no global x declaration, then it is a local variable.

If you think about declaration as a thing that is done at runtime, then you will get confused because the declaration travels back in time from unreachable positions:

julia> x=1;

julia> function f()
       return x
       #x=2
       end;

julia> f()
1

julia> function f2()
       return x
       x=2
       end;

julia> f2()
ERROR: UndefVarError: x not defined

In reality, the problem of f2() is not that x is not defined; julia knows perfectly well that you declared a local variable called x, which shadows the global x. Rather it is that you tried to use the value before initializing it.

1 Like

There’s the answer to my problem. Thank you!

I have a module containing several functions. All the functions need to refer to a global variable defined in the module but outside of the functions. Let’s call it x. After hours of trial and error, I still didn’t understand what was going on and why only one version worked correctly.

It seemed to me that I should use the global qualifier at each place that x is used is used, but that doesn’t work. Julia complains about extraneous “global” keywords. What I didn’t notice was that the configuration that works has “global” at the point in one of the functions where the value of x is assigned. In all other occurences, x is used but not assigned.

The fact that Julia makes an assumption did not occur to me.

So, if x is assigned a new value inside of a function, you have to use “global”. If x is used and not defined within the function, Julia assumes it must be the “outer x” that’s being referred to.

My intuition says that’s a good way to introduce subtle bugs, but at least I now understand it. I’d much prefer having to specify “global” at each place that the outer variable is used or assigned in an inner scope.

Thank you again for solving my mystery. :slight_smile: