Syntax of exchanging values of 2 variables seems to be broken in short-circuit evaluation

Tested on Julia 1.7.2:

julia> t1 = rand(4)
4-element Vector{Float64}:
 0.8846483727403565
 0.5933843798087087
 0.23231684762850824
 0.6599451690863327

julia> t2 = rand(5)
5-element Vector{Float64}:
 0.9708033624024174
 0.9204864722716941
 0.07792097172573631
 0.43291967469455217
 0.7960005316340157

julia> t1, t2 = t2, t1
([0.9708033624024174, 0.9204864722716941, 0.07792097172573631, 0.43291967469455217, 0.7960005316340157], [0.8846483727403565, 0.5933843798087087, 0.23231684762850824, 0.6599451690863327])

julia> t1
5-element Vector{Float64}:
 0.9708033624024174
 0.9204864722716941
 0.07792097172573631
 0.43291967469455217
 0.7960005316340157

julia> t2
4-element Vector{Float64}:
 0.8846483727403565
 0.5933843798087087
 0.23231684762850824
 0.6599451690863327

julia> true && (t1, t2 = (t2, t1))
(t1 = [0.9708033624024174, 0.9204864722716941, 0.07792097172573631, 0.43291967469455217, 0.7960005316340157], t2 = ([0.8846483727403565, 0.5933843798087087, 0.23231684762850824, 0.6599451690863327], [0.9708033624024174, 0.9204864722716941, 0.07792097172573631, 0.43291967469455217, 0.7960005316340157]))

julia> t1
5-element Vector{Float64}:
 0.9708033624024174
 0.9204864722716941
 0.07792097172573631
 0.43291967469455217
 0.7960005316340157

julia> t2
4-element Vector{Float64}:
 0.8846483727403565
 0.5933843798087087
 0.23231684762850824
 0.6599451690863327

julia> true && ((t1, t2) = (t2, t1))
([0.8846483727403565, 0.5933843798087087, 0.23231684762850824, 0.6599451690863327], [0.9708033624024174, 0.9204864722716941, 0.07792097172573631, 0.43291967469455217, 0.7960005316340157])

julia> t1
4-element Vector{Float64}:
 0.8846483727403565
 0.5933843798087087
 0.23231684762850824
 0.6599451690863327

julia> t2
5-element Vector{Float64}:
 0.9708033624024174
 0.9204864722716941
 0.07792097172573631
 0.43291967469455217
 0.7960005316340157

The values of t1 and t2 are not correctly exchanged when it’s part of the short-circuit evaluation unless I put parentheses on both sides of =. Moreover, the printed info of true && (t1, t2 = (t2, t1)) does not even match the output values. In the printed info, it’s suggested that the operation is

julia> true && (t1; t2 = (t2, t1))
([0.9708033624024174, 0.9204864722716941, 0.07792097172573631, 0.43291967469455217, 0.7960005316340157], [0.8846483727403565, 0.5933843798087087, 0.23231684762850824, 0.6599451690863327])

However, in this case the printed info is incorrectly corresponding to true && (t1, t2 = (t2, t1)).

I think this is a bug? Thank you!

I tried

t1 = 1
t2 = 2
t1, t2 = t2, t1
@show t1 t2
true && (t1, t2 = t2, t1)

and get the error message

ERROR: LoadError: syntax: field name "t1" repeated in named tuple 

which (surprisingly?) indicates that the parser interprets the same construction differently in that context.

Yeah, I also noticed this error that’s why I tried true && (t1, t2 = (t2, t1)) instead. It prompts no error but gives the wrong result.

This is because of named tuple syntax.

Notice (t1, t2 = (t2, t1)) is short for (t1 = t1, t2 = (t2, t1)) and results in NamedTuple{(:t1, :t2)}((t1, (t2, t1))).

So you’ll need to disambiguate a (named) tuple literal from a ()-grouped expression. You could do this like so:

true && ((t1, t2) = (t2, t1)) # an expression, not a tuple, since no comma

true && begin # explicit block
    t1, t2 = t2, t1
end

So this isn’t a bug — just confusion about Julia’s (very carefully designed) syntax!

5 Likes

Thanks a lot! I haven’t got familiar enough with named Tuple syntax.

I think the printed info was a bit confusing here because I first thought t2 got re-bound to the value of (t1, t2), but it turned out that the evaluation just returned a new named tuple that has a field .t2. The original t2 was never altered. A clearer demonstration:

julia> t1 = 1
1

julia> t2 = 2
2

julia> t = ( true && (t1, t2 = (t2, t1)) )
(t1 = 1, t2 = (2, 1))

julia> t.t1
1

julia> t.t2
(2, 1)

julia> t1
1

julia> t2
2
1 Like

For the future, of you choose your examples a bit more carefully, these things will be easier to debug.

julia> t1, t2 = 1, 2;

julia> true && (t1, t2 = (t2, t1))
(t1=1, t2=(2, 1))

This example takes up less horizontal real estate, and doesn’t rely on remembering random arrays of numbers.

2 Likes