Search code examples
functionargumentsjuliascoping

Julia scoping: why does this function modify a global variable?


I'm a relative newcomer to Julia, and so far I am a fan of it. But coming from years of R programming, some scoping rules leave me perplexed.

Let's take this function. This behaves exactly as I would expect.

function foo1(x)
    y = x
    t = 1
    while t < 1000
      t += 1
      y += 1
    end
    return 42
end

var = 0;
foo1(var)
# 42
var
# 0

But when doing something similar on an array, it acts as a mutating function (modifies it argument in the global scope!)

function foo2(x)
    y = x
    t = 1    
    while t < 1000
      t += 1
      y[1] += 1
    end
    return 42
end

var = zeros(1);
foo2(var)
# 42
var
# 999.0

I realize I can fix this by changing the first line to y = copy(x) , but what is the reason for such a (dangerous?) behaviour in the first place?


Solution

  • I would write an answer to this, but I think John Myles White has already done it better than I ever could so I'll just link to his blogpost:

    https://www.juliabloggers.com/values-vs-bindings-the-map-is-not-the-territory-3/

    In short x = 1 and x[1] = 1 are very different operations. The first one is assignment—i.e. changing a binding of the variable x—while the second is a syntactic sugar for calling the setindex! function, which, in the case of arrays, assigns to a location in the array. Assignment only changes which variables refer to which objects and never modifies any objects. Mutation only modifies objects and never changes which variables refer to which objects. This answer has a bit more detail about the distinction: Creating copies in Julia with = operator.