Why does `let` allow comma-separated assignments?

This is something I’ve always wondered… Why does a let block allow comma-separated assignments?

let x = 1, y = 2, z = 3
    (x, y, z)
end

As far as I can tell, this is completely identical to

let x = 1; y = 2; z = 3
    (x, y, z)
end

or

let
    x = 1
    y = 2
    z = 3
    (x, y, z)
end

This also doesn’t seem (?) to change the scoping rules:

a = 1
let x = a
    x
end

# equivalent to
let
    x = a
    x
end

Furthermore, let evaluates the expressions in order, so you can do

let x = 1, y = x + 1, z = y + 1
    (x, y, z)
end

which is actually the same as if you had ;-separated expressions. So… why not just use ; here, rather than using the alternate behavior of ,?

I find this a bit strange because in a begin block, or simply typed to the REPL, the expression x = 1, y = 2, z = 3 is invalid. It’s also weird because (x = 1, y = 2, z = 3) normally instantiates a named tuple, rather than assigning variables (or for defining default arguments of a function).

Is this some vestigial syntax from earlier Julia versions? Or perhaps I am missing something subtle about the scoping rules here?

2 Likes

they are not quite identical. consider

julia> let
           x = 0
           let
               x = 1
           end
           x
       end
1

julia> let
           x = 0
           let x = 1 end
           x
       end
0

assignments within a let-block follow normal local-variable scoping and assignment rules

assignments on the same line as let and comma-separated follow the same scoping rules as function argument binding, so let x=1; do_stuff; end is akin to (x->do_stuff)(1)

4 Likes

Very weird… because

julia> x = 0
0

julia> let
           x = 1
       end
1

julia> x
0

So something about being within the outer let block overrides the inner one?

Or even

julia> begin
           x = 0
           let
               x = 1
           end
           x
       end
0

julia> let
           x = 0
           let
               x = 1
           end
           x
       end
1

I complain about this here

and somewhat relatedly here

2 Likes

this is because REPL (and the begin) block form a global, not local scope. so the second assignment in your let block follows the local-vs-global rules, not local-vs-parent-local

if you ran that code from a .jl instead of in the REPL, I (think) you would see a warning printed

Gotcha, thanks. Super weird stuff!

So basically it’s kind of like this? (see annotations)

julia> begin
           x = 0     # global
           let
               x = 1 # local 1
           end
           x         # global
       end
0

julia> let
           x = 0     # local 1
           let
               x = 1 # local 1
           end
           x         # local 1
       end
1

and then, with the assignments on the same line:

julia> begin
           x = 0     # global
           let x = 1 # local 1
           end
           x         # global
       end
0

julia> let
           x = 0     # local 1
           let x = 1 # local 2
           end
           x         # local 1
       end
0

Finding it hard to wrap my head around this. Especially the fact that the innermost let changes its behavior because it exists within an outer let. (example 2 in the first code block)

I don’t see such a warning. (Maybe related to @jar1’s issues.)

1 Like

those annotations seem about right. note that local 2 shadows local 1 so you lose access to the “surrounding” x within the second let block. in Python one could use the keyword nonlocal to access local 1 but Julia does not support this (intentionally, I think)

ah, nvm. that warning is only for soft-scope assignments

1 Like

This interesting (but somewhat cryptic) discussion make me wish for a macro @scope that would just print the scope of the variable when the line is evaluated. Something in the spirit of:

julia>begin
          @scope x = 0
          @scope let x = 1
          end
          x
       end
In line 2: x = 0: global scope of module Main
In line 3: x = 1: local scope (nesting level 1) of function #...

Disclamer:
If there’s such macro, I’m not aware of it.
I’m not skilled enough to dig that deep into julia’s code and make a PR.

1 Like