Confused about global vs local scoping in for loops in 1.0


#1

This global vs local scoping really has me confused.

I understood that for loops have their own scope, so I was expecting this to error out, but it doesn’t.

julia> temp = 10
10

julia> for i in 1:5
           println(temp)
       end
10
10
10
10
10

Since I didn’t declare the variable temp inside the for loop, why didn’t this error out?


#2

Here temp is a global variable and in Julia you can read from global variables anywhere (in the same module - here Main) just not assign to them. If you want to assign to a global variable then you need to declare it with a global statement.


#3

From https://docs.julialang.org/en/v1/manual/variables-and-scoping/#man-scope-table-1:

"In a local scope, all variables are inherited from its parent global scope block unless:

* 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 but not for writing: [snip]
An explicit global is needed to assign to a global variable: [snip]"


#4

OK, I think I see. what is outside the for loop is defaulted to global, but what is defined within the for loop is local so after the loop completes, I cannot get those variables out, but I can get upstream global variables into the for loop to use.

I know there is another whole topic on this, but this is really unintuitive for people coming from other scripting languages.

Thanks for the help.


#5

I agree. But it is really complicated. Difficult to decide for which use case to optimize.


#6

Yes, a number of people think similarly and so I think it’s likely to change in the (near-ish?) future if I read the other thread correctly.


#7

Not wanting to add more noise to this topic, but just how I think about this.

A function is like a black box so variables go in (through the function arguments) and variables go out through return statements, so that’s makes sense that functions that their own scope.

But for, try, while blocks don’t really have an “input port” and neither do they have an “output port”. It would be different if there were something like a return statement to get stuff processed in those objects out but I don’t think there is, is there (other than setting those variables as global). It’s kind of like creating a black box with only a clear mechanism to get things in but not out.

What about If/else blocks. Why don’t those have local scoping rules similar to for/while/try?


#8

I don’t understand this statement. If you write global temp = 20 within the for-loop then there is a very clear mechanism to get things out, isn’t it?


#9

I mentioned, “other than setting variables as global” in my statement. But that feels like a workaround and seems kind of heavy handed. It feels like there should be another way other than resorting to globals.

Anyways, I don’t think I’m going to add anything useful to this discussion. I don’t have enough experience or background on programming languages. I’m just a simple user.

Anyways, thanks for taking the time to reply to this lowly user (me). I guess I’ll figure this out eventually.


#10

…I’m also ‘just a simple user’ :wink:


#11

I also find the new policy about global scopes and for loops quite complicated and confusing. Sure it can be justified (everything can, in the end), but would really like to change it back to what it was before the big changes of 0.7.0 (my two cents).
Still somebody here pointed out that there is a big thread discussing that… can somebody please tell me which one is it? This forum is quite busy and it is sometimes hard to keep up with everything…
Best regards and thanks,
Ferran.


#12

is one of the latest ones.


#13

It is a little more complicated!

julia> for i in 1:1
         temp = 2  # this is local
         for j in 1:1
            temp = 3  # this is not defaulted to global! 
         end
         println(temp)  # local is changed
       end
       println(temp) # global is undefined
3
ERROR: UndefVarError: temp not defined

Same if for is inside function body where also inherit function’s local scope.

And maybe this is bug:

julia> temp = 1
       for i in 1:1
         temp = 2
         for j in 1:1
            global temp = 3  # you could not reach global `temp` here! 
         end
         println(temp)
       end
       println(temp)
ERROR: syntax: `global temp`: temp is a local variable in its enclosing scope

#14

Keep simple things simple by default…


#15

see also Understanding while loop UndedVarError and scope

Good question


#16

You can greatly simplify things by writing functions for everything. Pass in everything the function needs as a parameter ( wrap them in tuples or struct if there are too many. ). For complex return values, use tuples and structs as well.
In general use struct and not mutable struct as much as possible.
I’ve happily never hit the problem with scoping and look on bemusedly at the amazingly long discussions on an issue I didn’t know existed.


#17

This is basically telling people not to use Julia interactively.


#18

Interactively. That’s how I use Julia. I develop extensively using the repl. Julia easily has the nicest repl-based function writing experience I’ve seen.

What do you mean by interactively?

By interactively I’m guessing you mean manipulating global state via expressions instead of functions?

Not sure why you would want to do that. So many pitfalls. There is a wealth of higher order functions available with very nice syntax. Broadcasting for example does an amazing amount. map reduce filter, it’s all there. Super simple anonymous functions. I do most of my work with hof.

Sure every now and then I interactively modify some global state directly, but it’s almost always pretty simple stuff setting up data for a function to process.

Function closures go a long way as well.

function thinkofname()
   M[1,1] = 0
   M[2,2] = 0
end

if you don’t like thinking of names

(()-> begin
   M[1,1] = 0
   M[2,2] = 0
   whatever expressions block you would normally enter
end)()

use ans immediately to evaluate your lambda expression with a closure mutating.

julia> A = rand(10,2)
10×2 Array{Float64,2}:
 0.674215   0.679194
 0.946359   0.26367 
 0.328985   0.119864
 0.793516   0.613542
 0.0598779  0.407411
 0.637759   0.233309
 0.172873   0.222074
 0.650882   0.91787 
 0.217435   0.8771  
 0.141181   0.853914

julia> ()->
   for i in 1:10
      A[i,1] = i
   end
#41 (generic function with 1 method)

julia> ans()

julia> A
10×2 Array{Float64,2}:
  1.0  0.679194
  2.0  0.26367

#19

This thread really does not require any more discussion.