Search code examples
for-loopscopejuliaidentifierscoping

julia: Using an index identifier twice in a nested for


By accident I used the same counter twice in a nested for loop:

for i in 1:2
    println(i)
    for j in 1:2
        for i in 5:6
            i += 1
            println(i, " ", j)
        end
    end
end

Which compiled without issue, and produced

1
6 1
7 1
6 2
7 2
2
6 1
7 1
6 2
7 2

It took me by surprise once I realized what was going on. Is this behavior intended? Since this leads to unexpected behavior, is there any way to make the compiler flag this as an error?

Just as an example, Fortran throws an error when compiling similar code:

program testfor

integer :: i, j, k

do i = 1,2
    do j = 1,2
        do i = 5,6
            i = i+1
            write(*,*) i, j
        end do
    end do
end do

end program testfor

Save as ftest.f90, then compiling with $:gfortran ftest.f90 -o ftest shows two errors:

ftest.f90:7:18:

    5 | do i = 1,2
      |          2        
    6 |     do j = 1,2
    7 |         do i = 5,6
      |                  1
Error: Variable 'i' at (1) cannot be redefined inside loop beginning at (2)
ftest.f90:8:19:

    7 |         do i = 5,6
      |                  2 
    8 |             i = i+1
      |                   1
Error: Variable 'i' at (1) cannot be redefined inside loop beginning at (2)

Solution

  • Short answer: yes, it is fully intended and absolutely reasonable and consistent.

    Long answer: There are two things going on in your code that result in the observed behavior, and which are worth pointing out.

    1. Name shadowing (as also mentioned in the comments above). There are in fact three separate variables here, two of which happened to be called i. And while the inner i is within scope, there is no way to access the outer i.

    2. Range loops. From the way you have written your code, I assume you expect this sort of for loop to run as it would in C, for example: let's have a loop variable, which is incremented in every cycle. At least the explicit extra increment in your innermost loop suggests this. In Julia, however, for loops iterate over iterable objects, in this case, the Range object. And the most important part: the current state of the iteration is stored in a hidden variable. The value of the variable you iterate with has absolutely no affect on the state. Therefore, neither incrementing i explicitly has an effect on the state of the inner loop, nor shadowing i has an effect on the state of the outer loop. Both loops keep chugging along the predetermined ranges.