# A slight confusion with a \`let\` block

**URL:** https://discourse.julialang.org/t/a-slight-confusion-with-a-let-block/122726
**Category:** General Usage
**Created:** [November 17, 2024, 1:50am UTC](https://discourse.julialang.org/t/a-slight-confusion-with-a-let-block/122726 "2024-11-17T01:50:42Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 17, 2024, 1:50am UTC](https://discourse.julialang.org/t/a-slight-confusion-with-a-let-block/122726/1 "2024-11-17T01:50:42Z")

</div>

Playing around the REPL, I got confused by the following (Julia 1.11):

```julia
julia> using StaticArrays

julia> L = 3;

julia> let L = 5, Q = @MVector [false for i in 1:L]
       @show L, Q
       end
(L, Q) = (5, Bool[0, 0, 0])
(5, Bool[0, 0, 0])

julia> let L = 5, Q = falses(L)
       @show L, Q
       end
(L, Q) = (5, Bool[0, 0, 0, 0, 0])
(5, Bool[0, 0, 0, 0, 0])

```

Essentially, the macro in the `let` block used the global value, and the non-macro assignment used the newly assigned `L`.  
Is this confusing or expected? Any pointer to a previous discussion?

---

<div class="post-metadata">

### Author: ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)
#### Post date: [November 17, 2024, 1:56am UTC](https://discourse.julialang.org/t/a-slight-confusion-with-a-let-block/122726/2 "2024-11-17T01:56:24Z")

</div>

> [@Dan](#):
>
> Essentially, the macro in the `let` block used the global value, and the non-macro assignment used the newly assigned `L`.

interesting, looks like it’s something specific to MVector:

```julia
julia> L = 3;

julia> let L=5, M = @show L
       @show L, M
       end;
L = 5
(L, M) = (5, 5)

julia> L
3

```

digging deeper:

```julia
julia> @macroexpand @show L
quote
    Base.println("L = ", Base.repr(begin
                #= show.jl:1229 =#
                local var"#34#value" = L
            end))
    var"#34#value"
end

julia> @macroexpand @MVector [false for i in 1:L]
quote
    #= /home/akako/.julia/packages/StaticArrays/9Yt0H/src/SVector.jl:65 =#
    let
        #= /home/akako/.julia/packages/StaticArrays/9Yt0H/src/SVector.jl:66 =#
        var"#35#f"(i) = begin
                #= /home/akako/.julia/packages/StaticArrays/9Yt0H/src/SVector.jl:66 =#
                false
            end
        #= /home/akako/.julia/packages/StaticArrays/9Yt0H/src/SVector.jl:67 =#
        (MVector){3}((tuple)(var"#35#f"(1), var"#35#f"(2), var"#35#f"(3)))
    end
end

```

it looks like `@show` would preserve the `local var_blah = L` and return this `local var_blah` where in `@MVector` it got eagerly evaluated to `3`

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 17, 2024, 2:01am UTC](https://discourse.julialang.org/t/a-slight-confusion-with-a-let-block/122726/3 "2024-11-17T02:01:59Z")

</div>

> [@jling](#):
>
> interesting, looks like it’s something specific to MVector:

Yep, an equivalent result to `falses(L)` can be done by removing the macro call:

```julia
julia> let L = 5, Q = [false for i in 1:L]
       @show L, Q
       end
(L, Q) = (5, Bool[0, 0, 0, 0, 0])
(5, Bool[0, 0, 0, 0, 0])

```

This is what the `MVector` macro does:

```julia
macro MVector(ex)
    static_vector_gen(MVector, ex, __module__ )
end

```

And this is what `static_vector_gen` does to `:comprehension` expressions like the example:

```julia
    elseif head === :comprehension
        #= omitted some input validation =#
        ex = ex.args[1]
        #= omitted some input validation =#
        rng = Core.eval(mod, ex.args[2].args[2])
        exprs = (:(f($j)) for j in rng)
        return quote
            let
                f($(esc(ex.args[2].args[1]))) = $(esc(ex.args[1]))
                $SV{$(length(rng))}($tuple($(exprs...)))
            end
        end

```

`rng = Core.eval(mod, ex.args[2].args[2])` evaluates `1:L` in the global scope because it needs a preexisting value while still parsing the `let` expression to put a number of arguments into the tuple call jling showed. This also causes problems if the global value would be instantiated too late, like if this were a `begin` block defining `L` before a `@MVector` call. This is documented behavior, per the docstring for `@SArray`:

```julia
 2. comprehensions
       │ Note
       │
       │ The range of a comprehension is evaluated at global scope by the macro, and must be made of
       │ combinations of literal values, functions, or global variables.

```

The most direct Github issue for local variables in comprehension ranges is #26, but it was closed in favor of the wider #97. The workaround given there is to skip the macro:

```julia
julia> let L = 5, Q = MVector{L}(false for i in 1:L)
       @show L, Q
       end
(L, Q) = (5, Bool[0, 0, 0, 0, 0])
(5, Bool[0, 0, 0, 0, 0])

```
