-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Description
I've started reading the Scoped Values page in the manual (v1.12-dev). It seems a bit rough around the edges.
1.
Since Julia uses lexical scope the variable
xis bound within the functionfto the global scope and entering a let scope does not change the valuefobserves.
This sentence seems to refer to the code block that immediately proceeds it, but it actually refers to the next code block. So it should probably say something like
In the following example, the variable
xis bound within ...
I don't want to propose an exact wording, I just want to point out that it's pretty confusing the way that it is written now.
2.
This example does not appear to be a good example of dynamic scoping:
const scoped_val = ScopedValue(1)
const scoped_val2 = ScopedValue(0)
# Enter a new dynamic scope and set value
@show scoped_val[] # 1
@show scoped_val2[] # 0
with(scoped_val => 2) do
@show scoped_val[] # 2
@show scoped_val2[] # 0
with(scoped_val => 3, scoped_val2 => 5) do
@show scoped_val[] # 3
@show scoped_val2[] # 5
end
@show scoped_val[] # 2
@show scoped_val2[] # 0
end
@show scoped_val[] # 1
@show scoped_val2[] # 0The exact same behavior can be achieved with let blocks:
const scoped_val = 1
const scoped_val2 = 0
@show scoped_val # 1
@show scoped_val2 # 0
let scoped_val = 2
@show scoped_val # 2
@show scoped_val2 # 0
let scoped_val = 3, scoped_val2 = 5
@show scoped_val # 3
@show scoped_val2 # 5
end
@show scoped_val # 2
@show scoped_val2 # 0
end
@show scoped_val # 1
@show scoped_val2 # 0(They print exactly the same thing to the REPL.)
3.
Since
withrequires a closure or a function and creates another call-frame, it can sometimes be beneficial to use the macro form.const STATE = ScopedValue{State}() with_state(f, state::State) = @with(STATE => state, f())
That sentence could use some elaboration, and I have no idea what the purpose of the example is (there is no explanation given).
4.
If I'm not mistaken, the @with in this example,
Base.@kwdef struct Configuration
color::Bool = false
verbose::Bool = false
end
const CONFIG = ScopedValue(Configuration())
@with CONFIG => Configuration(CONFIG[], color=true) begin
@show CONFIG[].color # true
@show CONFIG[].verbose # false
endshould actually be this:
@with CONFIG => Configuration(color=true) begin
@show CONFIG[].color # true
@show CONFIG[].verbose # false
endor maybe this:
@with CONFIG => Configuration(verbose=CONFIG[].verbose, color=true) begin
@show CONFIG[].color # true
@show CONFIG[].verbose # false
end5.
In the "Example" section, there is this sentence:
Other alternatives like task-local storage and global variables are not well suited for this kind of propagation; our only alternative would have been to thread a value through the entire call-chain.
Threading a value through the entire call-chain doesn't seem so bad to me. It's explicit (explicit is better than implicit) and it does not require a new language feature. Maybe you can make a better case in the manual for why ScopedValues are needed?