# Let block in an inner function captures variables from outer function

**URL:** <https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027>\
**Category:** General Usage\
**Created:** [March 13, 2023, 7:59pm UTC](https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027 "2023-03-13T19:59:43Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![artemsolod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/artemsolod/32/20704_2.png) [@artemsolod](https://discourse.julialang.org/u/artemsolod)\
**Post date:** [March 13, 2023, 7:59pm UTC](https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027/1 "2023-03-13T19:59:43Z")

</div>

I expected the following definitions `f1` and `f2` to be equivalent but `inner` in `f1` overwrites `state` variable from its outer scope despite being assigned in a `let`-block. Just trying to understand whether this is a bug? Tested on `1.8.5` and `1.9-rc1`. Thanks!

```julia
function f1(x)
    state = 0
    inner(y) = let
        state = y # overwrites state
        state
    end
    inner(x)
    state
end

function f2(x)
    state = 0
    function inner(y)
        let state = y # fresh state variable
            state
        end
    end
    inner(x)
    state
end

julia> f1(1)
1

julia> f2(1)
0

```

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [March 13, 2023, 8:13pm UTC](https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027/2 "2023-03-13T20:13:52Z")

</div>

That’s not a bug, but a feature (?) of the `let` syntax, i.e., let only introduces new bindings for the variables declared in a comma separated list on the _same line_ as the let itself (otherwise let is just like begin):

```julia
julia> function f1(x)
           state = 0
           inner(y) = let state = y # on same line => new binding for state
               state
           end
           inner(x)
           state
       end
f1 (generic function with 1 method)

julia> f1(1)
0

```

This is one of the few cases where Julia syntax is white-space sensitive. Don’t ask me why the parser works like that … having been bitten by this several times, I tend to use let much less than I would in Lisp or so.

---

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [March 13, 2023, 9:06pm UTC](https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027/3 "2023-03-13T21:06:38Z")

</div>

You mean new `scope`, do you?

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [March 13, 2023, 11:30pm UTC](https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027/4 "2023-03-13T23:30:16Z")

</div>

You’re right. `let` always introduces a new local scope, i.e., unlike `begin` which does not, and in addition creates new bindings for the variables in the same line as let (and only for those):

```julia
julia> function outer()
           # scope with locals x, y
           x = 1
           y = 2
           @show :before, x, y
           let x = 3 # new binding for x
               y = 4 # assignment to existing local
               @show :let, x, y
           end
           @show :after, x, y
       end
outer (generic function with 1 method)

julia> outer()
(:before, x, y) = (:before, 1, 2)
(:let, x, y) = (:let, 3, 4)
(:after, x, y) = (:after, 1, 4)
(:after, 1, 4)

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 13, 2023, 11:55pm UTC](https://discourse.julialang.org/t/let-block-in-an-inner-function-captures-variables-from-outer-function/96027/5 "2023-03-13T23:55:12Z")

</div>

> [@bertschi](#):
>
> and only for those

Unless you use the `local` keyword 😉

I like to think of the semantics of `let` as similar to function definitions with default argument values:

```julia
let a=1, b=1
    let a=2
        b=2
    end
    a, b # 1, 2
end

```

similar to:

```julia
((a=1, b=1)->begin
    ((a=2)->begin
        b=2
    end)()
    a, b # 1, 2
end)()

```

except that `let` doesn’t introduce boxes, but it also doesn’t introduce an inference barrier.
