Description
Introduction
The new ScopedValue
feature is really nice, but I think the name and the description of the feature in terms of dynamic scope is likely to lead to confusion.
There was a claim on Discourse that the PR had "an extensive analysis and bikeshedding of the naming". However, I have read through the entire PR and I disagree with that statement. There is some discussion of the name, but it is not a very complete discussion. In particular, it is surprising that there was no discussion regarding using the terminology "context" instead of "dynamic scope". In PR #35833, @tkf pointed out that the "context" terminology is used in at least four major programming languages:
- Python:
contextvars
- .NET:
ExecutionContext
- Kotlin:
CoroutineContext
- Go:
Context
Julia is a lexically scoped language, and the Scoped Values PR does not change that. Below are a couple examples that I think emphasize that what ScopedValue
provides is not dynamic scoping.
(Disclaimer: I have only tested these using ScopedValues.jl. I need to install juliaup
so I can get nightly.)
Example 1
julia> using ScopedValues
julia> f() = a[];
julia> function g()
a = ScopedValue(1)
f()
end;
julia> g()
ERROR: UndefVarError: `a` not defined
Stacktrace:
[1] f()
@ Main ./REPL[2]:1
[2] g()
@ Main ./REPL[3]:3
[3] top-level scope
@ REPL[4]:1
Example 2
julia> module A
f() = a[]
export f
end
Main.A
julia> module B
using ScopedValues
using ..A
a = ScopedValue(1)
g() = f()
export g
end
Main.B
julia> using .B
julia> g()
ERROR: UndefVarError: `a` not defined
Stacktrace:
[1] f()
@ Main.A ./REPL[1]:2
[2] g()
@ Main.B ./REPL[2]:5
[3] top-level scope
@ REPL[4]:1
Discussion
Both of these examples would work if ScopedValue
provided true dynamic scoping. So essentially we are telling the user that Julia uses a subtle combination of lexical scoping and dynamic scoping. I think that's more confusing than it needs to be. We should describe this feature as a "context" feature rather than a "dynamic scoping" feature. This is about more than just the name, it is about our semantic understanding of what the feature provides.
So, after a renaming, this feature could look like this:
f() = a[]
a = ContextValue(1)
newcontext(a => 2) do
f()
end # returns 2
Of course the precise naming is up for debate. Possible names for the context entering function:
with
withcontext
context
incontext
updatecontext
newcontext
I think updatecontext
and newcontext
are probably the most accurate names to describe what they do.