# Bug or feature: nested let variable scope

**URL:** <https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613>\
**Category:** New to Julia\
**Created:** [August 27, 2020, 7:00am UTC](https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613 "2020-08-27T07:00:31Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![systems](https://avatars.discourse-cdn.com/v4/letter/s/65b543/32.png) [@systems](https://discourse.julialang.org/u/systems)\
**Post date:** [August 27, 2020, 7:00am UTC](https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613/1 "2020-08-27T07:00:31Z")

</div>

I was expecting the second and third code to behave exactly like first, except neither behave the same

```julia
# First 
i = 0

let 
    i = 1
    Fs = Vector{Any}(undef, 2)

    while i <= 2
        let 
            j = i 
            Fs[j] = ()->j
        end 
        i += 1
    end

    println("$(Fs[1]())")
    println("$(Fs[2]())")
    println("inner $i")
end

println("global $i")

#= output
 1
 2
 inner 3
 global 0
=#

```

```julia
# Second 
i = 0

let 
    i = 1
    Fs = Vector{Any}(undef, 2)

    while i <= 2
        let 
            i = i 
            Fs[i] = ()->i
        end 
        i += 1
    end

    println("$(Fs[1]())")
    println("$(Fs[2]())")
    println("inner $i")
end

println("global $i")

#= output
 3
 3
 inner 3
 global 0
=#

```

```julia
# Third

i = 0

let 
    local i = 1
    Fs = Vector{Any}(undef, 2)

    while i <= 2
        let 
            local i = i 
            Fs[i] = ()->i
        end 
        i += 1
    end

    println("$(Fs[1]())")
    println("$(Fs[2]())")
    println("inner $i")
end

println("global $i")

#=
ERROR: LoadError: UndefVarError: #1#i not defined
Stacktrace:
 [1] top-level scope at C:\dev\lang\julia\tester3.jl:9
 [2] include(::Function, ::Module, ::String) at .\Base.jl:380
 [3] include(::Module, ::String) at .\Base.jl:368
 [4] exec_options(::Base.JLOptions) at .\client.jl:296
 [5] _start() at .\client.jl:506
in expression starting at C:\dev\lang\julia\tester3.jl:3
=#

```

- I assumed that local is redundant in a let block, and that you only need to specify global otherwise local will be assumed
- the top let statement seem to be able to create a local i variable, but not the inner let
- The inner let uses the outer i and raises and error when i try to force to make a local copy

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [August 27, 2020, 9:16am UTC](https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613/2 "2020-08-27T09:16:00Z")

</div>

`let` blocks, similarly to loop bodies, introduce new scopes. By itself, making a new scope does not create new local bindings for names that exist in the outer local scope. `let`, however, has an option to create them by listing the names that must shadow the outer bindings on the same line with the word `let`. Compared to `local` declarations, the difference with `let` is that the rhp of `let <var> = <expr>` is evaluated in the enclosing scope, so that it can contain `<var>` (in contrast with, e.g., `local i = i`, which throws `UndefVarError`).

```julia
i = 0

let i = 1 # `i=1` on the same line
    Fs = []

    while i <= 2
        let i=i
            push!(Fs, ()->i)
        end
        i += 1
    end

    println("$(Fs[1]())")
    println("$(Fs[2]())")
    println("inner $i")
end

println("global $i")

#= output
 1
 2
 inner 3
 global 0
=#

```

---

<div class="post-metadata">

**Author:** ![systems](https://avatars.discourse-cdn.com/v4/letter/s/65b543/32.png) [@systems](https://discourse.julialang.org/u/systems)\
**Post date:** [August 27, 2020, 1:34pm UTC](https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613/3 "2020-08-27T13:34:37Z")

</div>

> [@Vasily\_Pisarev](#):
>
> `let <var> = <expr>` is evaluated in the enclosing scope

Thanks this is very clear, this line needs to be added to the docs

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [August 27, 2020, 2:06pm UTC](https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613/4 "2020-08-27T14:06:04Z")

</div>

That _is_ in the docs 🤷‍♂️ :

> [@](#):
>
> The `let` syntax accepts a comma-separated series of assignments and variable names:
> 
> ```julia
> let var1 = value1, var2, var3 = value3
> code
> end
> 
> ```
> 
> The assignments are evaluated in order, with each right-hand side evaluated in the scope before the new variable on the left-hand side has been introduced.

([Essentials · The Julia Language](https://docs.julialang.org/en/v1/base/base/#let))

---

<div class="post-metadata">

**Author:** ![systems](https://avatars.discourse-cdn.com/v4/letter/s/65b543/32.png) [@systems](https://discourse.julialang.org/u/systems)\
**Post date:** [August 27, 2020, 2:38pm UTC](https://discourse.julialang.org/t/bug-or-feature-nested-let-variable-scope/45613/5 "2020-08-27T14:38:04Z")

</div>

I think if they add

The assignments are evaluated in order, with each right-hand side evaluated in the scope **of the enclosing let block** before the new variable on the left-hand side has been introduced **in the new nested let block**

or something like it, I would have understood better

I remember reading this line, but I didn’t get it .  
The way you explained it clearer

in the line of let new local variable are introduced in the new let block but evaluated in the the scope of the enclosing let bock

I remember thinking why do I need to do

`let x , z , y`

when I cant just do

```julia
let 
  x
  z 
  y

```

and it look much cleaner , now I know because on the same line I have the enclosing scope available for my variable declaration, once I leave this line, i dont have them
