The fact that this is happening for you with a closure is actually a bit of a red herring. This is just the normal way that variables are bound in Julia. As you might already know, assigning a variable doesn’t create a copy, just a new name for the same object. So we can do:
julia> a = [1, 2, 3]
3-element Array{Int64,1}:
b = 1
2
3
julia> b = a
3-element Array{Int64,1}:
1
2
3
julia> a[1] = 5
5
julia> b
3-element Array{Int64,1}:
5
2
3
The value in b
changes because b
is just another binding for the same data as a
. Compare this with:
julia> a = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> c = a[1]
1
julia> a[1] = 5
5
julia> c
1
In that case, c
is bound to whatever was originally in a[1]
(the value 1
in this case). Changing a[1]
has no effect on c
because c
just points to the value 1
regardless of what a
happens to contain right now.
The reason this is confusing in your particular case is that the closure myF(t) = t * a[2]
creates a local binding for a
(like the first case) not a local binding for a[2]
(like the second case). That’s why changing a
changes the value returned by myF
.
The code from @dfdx works because calling the makeMyF
function creates a new local variable bound to a[2]
, and your closure captures that variable instead of capturing the entire a
array. If you don’t want to introduce a special makeMyF
function, you can also get the behavior you want with a let
block like this:
julia> a = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> myF = let
y = a[2]
t -> t * y
end
(::#1) (generic function with 1 method)
julia> myF(1)
2
julia> a[2] = 1
1
julia> myF(1)
2
julia> a = 0
0
julia> myF(1)
2
Note that the y
created inside the let
block just lives inside that block and doesn’t escape or interfere with any other variable named y
. Even creating a new variable that happens to also be called y
won’t break myF
:
julia> y = 10
10
julia> myF(1)
2