# Will my variable be modified in a \`do\` block? Very subtle to anticipate

**URL:** https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124
**Category:** General Usage
**Tags:** question, threads
**Created:** [July 28, 2025, 6:25am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124 "2025-07-28T06:25:02Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 6:25am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/1 "2025-07-28T06:25:02Z")

</div>

I notice this difference

```julia
julia> Threads.nthreads()
4

julia> my_lock = Threads.ReentrantLock()
ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))

julia> function my_dumb_fun(af)
       end;

julia> for k = 1:1
           Threads.@threads for i = 1:3
               a = -10i
               my_dumb_fun() do
                   a = 10i
               end
               println("at the end of i = $i, a = $a")
           end
       end
at the end of i = 1, a = -10
at the end of i = 3, a = -30
at the end of i = 2, a = -20

julia> for k = 1:1
           Threads.@threads for i = 1:3
               a = -10i
               Threads.lock(my_lock) do
                   a = 10i
               end
               println("at the end of i = $i, a = $a")
           end
       end
at the end of i = 1, a = 10
at the end of i = 3, a = 30
at the end of i = 2, a = 20

```

I don’t know how to explain.  
related [post](https://discourse.julialang.org/t/retrieve-a-jump-constraintref-from-within-a-do-block/131121/2)

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 7:20am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/2 "2025-07-28T07:20:57Z")

</div>

In the example with the `my_dumb_fun`, the do-block is never executed.

Try:

```julia-auto
function my_dumb_fun(af)
    af()
end

```

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 7:24am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/3 "2025-07-28T07:24:25Z")

</div>

Yes, it will be different.  
But my point was also on the scope rule of `do` block. Isn’t it a hard local scope so that the inner `a` will not affect the outer `a`?

```julia
julia> function my_dumb_fun(af)
           af()
       end;

julia> 

julia> for k = 1:1                                                                                              
           for i = 1:3                                                                                          
               a = -10i                                                                                         
               my_dumb_fun() do                                                                                 
                   a = 10i                                                                                      
               end                                                                                              
               println("at the end of i = $i, a = $a")                                                          
           end                                                                                                  
       end
at the end of i = 1, a = 10
at the end of i = 2, a = 20
at the end of i = 3, a = 30

```

Why is this?

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 7:41am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/4 "2025-07-28T07:41:54Z")

</div>

> [@WalterMadelim](#):
>
> But my point was also on the scope rule of `do` block. Isn’t it a hard local scope so that the inner `a` will not affect the outer `a`?

No. The do-block is just made into an anonymous function. So

```julia-auto
fun() do
    a = 14
end

```

is semantically equivalent to

```julia-auto
function someuniquename() 
    a = 14
end
fun(someuniquename)

```

So if a variable `a` exists in the scope of the do-block, that `a` is used. If you want a separate `a` it must be declared as `local`:

```julia-auto
my_dumb_fun() do
    local a = 10i
end

```

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 7:50am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/5 "2025-07-28T07:50:01Z")

</div>

This is actually one of the few things I strongly dislike with julia. If you happen to have a variable `a` outside the `@threads` loop, and use `a` inside, all the parallel tasks share the same `a`. It’s relatively harmless if `a` is only read inside the loop, and quite nice for setting up constants and whatever, but it is devastating if `a` is used as a temporary variable which is assigned to in the parallel tasks.

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 8:01am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/6 "2025-07-28T08:01:49Z")

</div>

~~But here is one immediate question—why doesn’t this work~~ Well, maybe don’t need to ask

```julia
julia> function my_dumb_fun(af)
           af()
       end;

julia> function someuniquename()
           a = 10i
       end;

julia> for _ = 1:1
           for i = 1:3
               a = -10i
               my_dumb_fun(someuniquename)
               println("at the end of i = $i, a = $a")
           end
       end
ERROR: UndefVarError: `i` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] someuniquename()
   @ Main .\REPL[2]:2
 [2] my_dumb_fun
   @ .\REPL[1]:2 [inlined]
 [3] top-level scope
   @ .\REPL[3]:4

```

* * *

To be honest, I am greatly shocked. Look at this image

 ![image](https://global.discourse-cdn.com/julialang/original/3X/9/2/92e1c1e2ca767b0fed704c2e44e9b6ca63aac81d.png)

My thought was aligned with ChatGPT’s, which is incorrect. I just realized that these are different

```julia
julia> for i = 1:3
           a = -10i
           let
               a = 10i
           end
           println("a = $a")
       end
a = 10
a = 20
a = 30

julia> for i = 1:3
           a = -10i
           let a = 10i
               a = 1a
           end
           println("a = $a")
       end
a = -10
a = -20
a = -30

```

* * *

So a `let` is very unlike a `function`, as shown in the follows

```julia
julia> for i = 1:2
           a = -10i
           let
               a = 10i
           end
           println("a = $a")
       end
a = 10
a = 20

julia> function my_let(i)
           a = 10i
       end;

julia> for i = 1:2
           a = -10i
           my_let(i)
           println("a = $a")
       end
a = -10
a = -20

```

I thought that they were the same.

* * *

> [@sgaure](#):
>
> is semantically equivalent to

Therefore I don’t think it is appropriate to make an analogy between `do` and `function` blocks, just like between `let` and `functions`. What’s your opinion?

Writing an `a = 10i` in the **body** of a `let` block, versus writing the same `a = 10i` in the body of a function, is very unlike.

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 8:25am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/7 "2025-07-28T08:25:57Z")

</div>

This is a slightly different phenomenon.

Your example with the function `my_let` is in the REPL, i.e. at the julia prompt. The scope rules are somewhat different there. The function `my_let` will not look in the global scope to find the `a` when you assign to it, only in outer _local_ scopes. For that to work, you would need `global a = 10i`.

Since the scoping rules are somewhat different at the REPL (outer level), it is good practice to enclose all code in functions.

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 8:31am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/8 "2025-07-28T08:31:40Z")

</div>

Consider this example:

```julia-auto
julia> a = 12
12

julia> function fun()
           function my_let(i)
               a = 10i
           end

           for i = 1:2
               a = -10i
               my_let(i)
               println("a = $a")
           end
       end
fun (generic function with 1 method)

julia> function fun2()
           function my_let(i)
               a = 10i
           end

           for i = 1:2
               a = -10i
               my_let(i)
               println("a = $a")
           end

           a = 42
           return nothing
       end
fun2 (generic function with 1 method)

julia> fun()
a = -10
a = -20

julia> fun2()
a = 10
a = 20

julia> a
12

```

In function `fun`, the `my_let` assigns to `a`, but there is no `a` in the outer scope (inside `fun`), because the `for` loop creates its own scope, so the `a` in `my_let` is local, and different from the one in the loop.

In function `fun2`, there is an `a` in the outer scope (the `a = 42` at the end), so the `a` in `my_let` and inside the loop is the same. It’s a bit contrived with the `a = 42` at the end, but imagine you write such a function, and you have various loops and functions which all are supposed to act on `a`. It would work just as expected.

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 8:57am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/9 "2025-07-28T08:57:50Z")

</div>

In my applications, I would typically work in the julia REPL, meaning that I would not purposefully wrap all my code within a function, since in that case debugging is inconvenient. Therefore in the preceding discussions you see my writing a dumb `for k = 1:1` loop at the outermost layer.

* * *

Even considering only the julia REPL context, the behavior of the `let` is rocket science! I assume that it is very hard for users to predict whether the `a = 10i` written within the body of `let` can modify an outer `a`.🥲

```julia
julia> let
           a = 0
       end;

julia> @isdefined a
false

julia> for i = 1:2
           a = -10i
           let # can modify outer
               a = 10i
           end
           println("i = $i, a = $a")
       end
i = 1, a = 10
i = 2, a = 20

julia> @isdefined a
false

julia> a = -10;

julia> let i = 1 # cannot modify outer
           a = 10i
       end;

julia> a
-10

julia> for _ = 1:1
           for i = 1:2
               a = -10i
               let # cannot modify outer
                   a = 10i
               end
               println("i = $i, a = $a")
           end
       end
i = 1, a = -10
i = 2, a = -20

```

I assume that the body of `do` are almost the same as the `let` case. Am I right?  
The head line of `do` and `let` both introduce local variables—I just realize it today.

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 9:23am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/10 "2025-07-28T09:23:02Z")

</div>

> [@WalterMadelim](#):
>
> In my applications, I would typically work in the julia REPL, meaning that I would not purposefully wrap all my code within a function, since in that case debugging is inconvenient.

Note that in addition to some annoying scoping rules, there’s another drawback with working at the REPL. Julia’s unit of compilation is the function. If your code is not in a function, it will not be compiled, but interpreted. Which means it will be slower by 1-3 orders of magnitude.

```julia-auto
julia> const n = 100000000
100000000

julia> @time "repl" begin
           s = 0.0
           for i in 1:n
               s += 1/i
           end
           s
       end
repl: 5.050498 seconds (200.00 M allocations: 2.980 GiB, 1.69% gc time)
18.997896413852555

julia> function sumthem(n)
           s = 0.0
           for i in 1:n
               s += 1/i
           end
           return s
       end
sumthem (generic function with 1 method)

julia> @time "function" sumthem(n)
function: 0.100164 seconds
18.997896413852555

```

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 10:51am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/11 "2025-07-28T10:51:07Z")

</div>

> [@sgaure](#):
>
> slower by 1-3 orders of magnitude

I see. Thanks for the advice, I’ll remember.

> [@sgaure](#):
>
> If you happen to have a variable `a` outside the `@threads` loop, and use `a` inside, all the parallel tasks share the same `a`. It’s relatively harmless if `a` is only read inside the loop, and quite nice for setting up constants and whatever, but it is devastating if `a` is used as a temporary variable which is assigned to in the parallel tasks.

Perhaps here is an example

```julia
julia> s = 0;

julia> for i = 1:3                                                                                                           
           s = i                                                                                                             
       end

julia> s # 3
3

julia> s = 0;

julia> Threads.@threads for i = 1:3                                                                                          
           s = i # maybe this usage is wrong                                                                                 
       end

julia> s # 0
0

```

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 11:04am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/12 "2025-07-28T11:04:21Z")

</div>

> [@sgaure](#):
>
> In the example with the `my_dumb_fun`, the do-block is never executed.

So you make judgements with the source code of my `my_dumb_fun`. This is to say, the scope inference about the `do` block is much more difficult. (I am not aware what the source code of the `Threads.lock` function is).

Therefore, if the circumstance is like this, I cannot make a deterministic inference

```julia
julia> function modify_a_randomly(af)                                                                                        
           rand() > .5 && return                                                                                             
           af()                                                                                                              
       end;

julia> for i = 1:5
           a = -10i
           modify_a_randomly() do
               a = 10i
           end
           println("i = $i, a = $a")
       end
i = 1, a = 10
i = 2, a = -20
i = 3, a = 30
i = 4, a = -40
i = 5, a = 50

```

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [July 28, 2025, 11:10am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/13 "2025-07-28T11:10:22Z")

</div>

> [@WalterMadelim](#):
>
> ```julia-auto
> julia> s = 0;
> 
> julia> Threads.@threads for i = 1:3                                                                                          
> s = i # maybe this usage is wrong                                                                                 
> end
> 
> julia> s # 0
> 0
> 
> ```

That one behaves differently inside a function:

```julia-auto
function fun()
    s = 0
    Threads.@threads for i = 1:3
        s = i
    end
    return s
end
julia> fun()
3

```

The content of the `@threads` loop is put inside an anonymous function by the `@threads` macro. It will look for an `s` in an outer _local_ scope. The `s` in the function is local, so it will be used.

On the other hand, when the `@threads` loop is defined at top-level, the outer scope with the `s` is global, so it will not be looked up.

The `for` loop without `@threads` is not in a function, it creates a soft scope, and works differently at the REPL.  
There’s a lengthy explanation in [Scope of Variables · The Julia Language](https://docs.julialang.org/en/v1/manual/variables-and-scoping/#on-soft-scope).

There are good reasons for the _put code in functions_ advice.

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [July 28, 2025, 11:28am UTC](https://discourse.julialang.org/t/will-my-variable-be-modified-in-a-do-block-very-subtle-to-anticipate/131124/14 "2025-07-28T11:28:09Z")

</div>

> [@sgaure](#):
>
> put code in functions

Thanks, I take your point.

* * *

(Update)

> [@sgaure](#):
>
> ```julia-auto
> fun() do
> a = 14
> end
> 
> ```
> 
> is semantically equivalent to
> 
> ```julia-auto
> function someuniquename() 
> a = 14
> end
> fun(someuniquename)
> 
> ```

Now I have owned a deeper understanding. Therefore share my opinion:

The problem with `do` syntax is that it has an underlying unintuitive implication

- passing the function defined in `do` block as the first argument

Therefore the odds are that

> [@sgaure](#):
>
> the do-block is never executed.

But in sheer appearance,

```julia-auto
fun() do
    a = 14
end

```

seems to suggest that `a = 14` is **do** ne at that line.

I browsed some discussions and find that there are also people who don’t get used to using `do` block. e.g. [here](https://discourse.julialang.org/t/boxing-obvious-cases-where-the-type-is-clear/107192/5).

So we can write

```julia
local function af()
    a = 14
end
fun(af)

```

in place of any `do` syntax.
