# How do ScopedValues actually work?

**URL:** <https://discourse.julialang.org/t/how-do-scopedvalues-actually-work/133270>\
**Category:** Internals & Design\
**Tags:** scopedvalues\
**Created:** [October 18, 2025, 8:57pm UTC](https://discourse.julialang.org/t/how-do-scopedvalues-actually-work/133270 "2025-10-18T20:57:19Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![benninkrs](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/benninkrs/32/3191_2.png) [@benninkrs](https://discourse.julialang.org/u/benninkrs)\
**Post date:** [October 18, 2025, 8:57pm UTC](https://discourse.julialang.org/t/how-do-scopedvalues-actually-work/133270/1 "2025-10-18T20:57:19Z")

</div>

I understand that a `ScopedValue` is a key into `Scope` (a wrapper for a `PersistentDict`) that is accessible by `Core.current_scope()`. I understand that `@with` creates a new `Scope` with the specified value. What I don’t understand is how that new `Scope` becomes what is returned by `Core.current_scope()`, and how the original `Scope` is restored when the body of the `@with` expression concludes. According to `base/scopedvalues.jl`, `@with` returns

```julia-auto
Expr(:tryfinally, esc(ex), nothing, :(Scope(Core.current_scope()::Union{Nothing, Scope}, $(exprs...))))

```

That expression doesn’t seem to correspond to any documented Julia control structure and I can’t see how it modifies and then restores what is returned by `Core.current_scope`. There seems to be some magic here.

---

<div class="post-metadata">

**Author:** ![ericphanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ericphanson/32/215186_2.png) [@ericphanson](https://discourse.julialang.org/u/ericphanson)\
**Post date:** [October 18, 2025, 9:25pm UTC](https://discourse.julialang.org/t/how-do-scopedvalues-actually-work/133270/2 "2025-10-18T21:25:56Z")

</div>

Julia’s Task object has a `scope` field so it knows what scope it belongs to, and this field is inherited from parent tasks. You can se that in the original PR that introduced them: [Scoped values by vchuravy · Pull Request #50958 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/50958/files). You’ll notice the `scope` field is replacing a `logstate` field: logger’s already had dynamic scope so that `with_logger` would apply the logger only to the tasks executing within it, rather than globally to other pre-existing tasks. ScopedValues generalized this feature and reimplemented the dynamic log scope in terms of a ScopedValue. And `with` resets the tasks `scope` field to whatever it was before the `with` block: [Scoped values by vchuravy · Pull Request #50958 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/50958/files#diff-b4c4923952f1d73fa36df5606e3dbf4aff00e037d2bd46d5bb23acaf1702b679R162).

(My links are based on the original PR so some things may have changed a bit since).

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [October 19, 2025, 5:18am UTC](https://discourse.julialang.org/t/how-do-scopedvalues-actually-work/133270/3 "2025-10-19T05:18:47Z")

</div>

We just stuck the compiler support for installing a scope into `:tryfinally`, because it’s conceptually similar in that it installs a task local value (exception stack vs scope stack), so it was easy to wire up. That’s considered an implementation detail though.

---

<div class="post-metadata">

**Author:** ![benninkrs](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/benninkrs/32/3191_2.png) [@benninkrs](https://discourse.julialang.org/u/benninkrs)\
**Post date:** [October 19, 2025, 6:00pm UTC](https://discourse.julialang.org/t/how-do-scopedvalues-actually-work/133270/4 "2025-10-19T18:00:47Z")

</div>

The code in the PR @ericphanson referred to,

```julia-auto
    quote
        ct = $(Base.current_task)()
        current_scope = ct.scope::$(Union{Nothing, Scope})
        ct.scope = $(Scope)(current_scope, $(exprs...))
        $(Expr(:tryfinally, esc(ex), :(ct.scope = current_scope)))
    end

```

is no longer in `scopedvalues.jl` and has been replaced with the expression I quoted above. And I see by doing a `dump` on the current task that there is indeed `scope` field, but it cannot be directly queried or assigned to. Combining this with @Keno 's response, I conclude that the code above was essentially moved under the hood into Julia’s internal implementation of the `:tryfinally` expression. So it is indeed “magic” in the sense of not being implemented entirely in visible Julia code.

As far as I am concerned the two of you together answered my question but I can only mark one as the solution.
