Syntax of multiple let assignments vs tuples

One question has been continually bothering me—why do we have this syntax let x = 1, y = 2?
Since we used to write x, y = 1, 2 directly in julia REPL.
Otherwise it’s invalid as follows

julia> x = 1, y = 2
ERROR: syntax: invalid assignment location "1" around REPL[7]:1
Stacktrace:
 [1] top-level scope
   @ REPL[7]:1

Then why do we need to invent the additional style x = 1, y = 2 and exclusively associate it to let?

My intuition was acute to find that we have this Cartesian For-loop usage.

for i = 1:2, j = 1:3

And this fact

julia> for i = 1 #


ERROR: ParseError:
# Error @ REPL[2]:2:2
for i = 1 #

#└ ── premature end of input
Stacktrace:
 [1] top-level scope
   @ none:1

Note that this ParseError is not as severe as the one let x = 1 # has, because the latter cannot tolerate an Enter stroke, whereas the former can.

You’re forgetting about the = in () syntax of named tuples and keyword arguments in function calls, though the latter parses to :kw subexpressions instead of :(=).

1 Like

Yes, I do forget them. Because I hardly use them.
I only write normal functions function f(a, b ,c) in my daily code.
And I almost never use named tuples either. :sweat_smile:

Seems I understand now. let/for can create a dependence tree, whereas x, y = 1, 2 style is not.

for i = 1:3, j = 1:i, k = 1:i+j
# some body
end
julia> let x = 3, y = 4, x = y, y = x
       println("x = $x, y = $y")
       end
x = 4, y = 4

julia> let x = 3, y = 4, (x, y) = (y, x)
       println("x = $x, y = $y")
       end
x = 4, y = 3

Is there a good reason that the second style is not supported?

julia> for _ = 1:3
           local x, y = 7, 8
           println("x = $x, y = $y")
       end
x = 7, y = 8
x = 7, y = 8
x = 7, y = 8

julia> for _ = 1:3
           local x = 7, y = 8
           println("x = $x, y = $y")
       end
ERROR: syntax: invalid assignment location "7" around REPL[2]:2
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

Because resembling const, local is valid only for the nearest =.

let and local do not signify assignments, but declarations with optional assignments. It’s therefore not very surprising that the rules differ somewhat from assignments. In general, julia’s syntax doesn’t distinguish between declarations and assignments, so it’s a bit fuzzy. for neither declares nor assigns, the = is merely a convenience because it has been in use for this since fortran 66. (you can use in or instead of = in for loops).

But there are various strange things in the julia parser, just like in many languages.
Here we create h(i) = 1, h(i) = 2, h(i) = 3 in quick succession:

julia> for h(i) in 1:3; display(h); end
(::var"#h#h##4"{Int64}) (generic function with 1 method)
(::var"#h#h##4"{Int64}) (generic function with 1 method)
(::var"#h#h##4"{Int64}) (generic function with 1 method)
1 Like

I think = in the for loop is aesthetically more agreeable than in, as we are writing code, not English texts. Moreover, it might make a math reader feel strange to read for a ∈ 1 println(a) end since 1::Int has no chance being a Set (but is iteratable).

While this is true in plain julia, it’s easy to let Ints behave somewhat like a set, in the von Neumann style of natural numbers:

julia> Base.iterate(i::Int, state=0) = state ≥ i ? nothing : (state, state+1)
julia> Base.IteratorSize(::Type{Int}) = Base.HasLength()
julia> Base.length(i::Int) = i
julia> Base.eltype(::Type{Int}) = Int
julia> for i in 1
           println(i)
       end
0
julia> collect(3)
3-element Vector{Int64}:
 0
 1
 2
1 Like