Unpacking in arglist vs inside function

Unpacking inside the function assigns into the parent scope; unpacking in the arglist binds a new variable. Should I expect that from something in Scope of Variables · The Julia Language or is that arglist-unpacking a special behavior?

let x = 1
    function f(θ)
        (;x) = θ
    end

    θ₀ = (;x=2)
    @show x # 1
    f(θ₀)
    @show x # 2
end

let x = 1
    function f((;x))        
    end

    θ₀ = (;x=2)
    @show x # 1
    f(θ₀)
    @show x # 1
end

cc @c42f

Edit: Unpacking in arglist vs inside function · Issue #51107 · JuliaLang/julia · GitHub

Which version are you on? They produce the same results on Julia 1.8.2:

julia> VERSION
v"1.8.2"

julia> let x = 1
           function f(θ)
               (;x) = θ
           end
       
           θ₀ = (;x=2)
           @show x # 1
           f(θ₀)
           @show x # 2
       end;
x = 1
x = 2

julia> let x = 1
           function f((;x))        
           end
       
           θ₀ = (;x=2)
           @show x # 1
           f(θ₀)
           @show x # 1
       end;
x = 1
x = 2

1.9.1

The behavior changed with 1.9.0, but I’m not sure it was on purpose. It isn’t mentioned in the change log. A git bisect turned up

To be fair, while to be expected if one studies the manual thoroughly and grasps the implication of

If a function argument name is written as a tuple (e.g. (x, y)) instead of just a symbol, then an assignment (x, y) = argument will be inserted for you:

in conjunction with the scoping rules, I find the fact that in the latter example, the call f((;x=2)) modifies (rather, used to modify) the parent scope a bit “insane”.

I am glad it has changed, but not too happy about the change being undocumented or possibly unintentional.

1 Like

I think the 1.8 behavior is correct and the 1.9 behavior is a bug. The 1.8 behavior is just local scoping rules in action. Admittedly it’s less obvious since the assignment is implicit.

3 Likes