Change `ScopedValue` default outside of `with`

Hi All,

I like the recently introduced ScopedValue type, but found myself wanting to be able to change it’s default value persistently outside of ScopedValues.with calls.

An example for this is that in GitHub - JuliaPluto/PlutoPlotly.jl I default to converting all numbers to Float32 to speed up and reduce memory of the generated plot objects, but I want to have a flag to be able to potentially disable this behavior when higher precision is needed. (e.g. this issue Large integers are truncated · Issue #61 · JuliaPluto/PlutoPlotly.jl · GitHub)

I thought I could achieve this by combining a RefValue and a ScopedValue toghther in a single type and make it behave the way I want it (i.e. act in most situation as a ScopedValue but allow o change its default value persistently).

I put my attempt into this very small package:

and I wanted to get feedback on whether:

  • There is already a way to achieve this either built in or via other existing packages
  • What I am doing in that package is either broken, unsafe or wrong in any other way
1 Like

One of the central premises of ScopedValue is that you observe the same value inside the scoped value as long as you have not executed a with operation along your dynamical execution.

It is totally fine to use a RefValue as a value for a ScopedValue. The only “benefit” I see here is that you avoid sv[][]. I would not implement this by making the default value special, but rather:

struct MutableScopedValue{T}
    sv::ScopedValue{RefValue{T}}
end
  • What I am doing in that package is either broken, unsafe or wrong in any other way

This looks odd ScopedRefValues.jl/src/ScopedRefValues.jl at 023fb1a97804fceebf43a496d478a85d03a14445 · disberd/ScopedRefValues.jl · GitHub since get does return the default value IIRC.

I also don’t like that setindex! is only w.r.t to the default value, this leads to the surprising behavior

with(srv => :x) do
    srv[] = :y
    @assert srv[] === :y # will fail
end

Which is why I would embrace mutability fully.

4 Likes

Thanks @vchuravy for the quick reply.
I hadn’t indeed thought about using a RefValue directly.

I updated the package mockup to follow the suggestion you gave but I am not so sure anymore whether it’s indeed worth it over just explicitly asking users to pass a RefValue explicitly to a normal ScopedValue for flags I want to make customizable in my packages.

2 Likes

I had a similar desire. The @with construct is fine for coding but not very convenient for interactive work. My thought was not to have a mutable value but rather to enter and leave scope with separate commands, something like

julia> @newscope x => 2.5

julia>  # some interactive coding

julia> @endscope

Based on the answers to my question How do ScopedValues actually work? I gather that scope changes are implemented in core Julia and thus something like this cannot be implemented in a package. But perhaps it is doable in principle?

Maybe ANN: ScopedSettings - ScopedValues with malleable global values meets your needs?

Yes, that’s kinda what I built ScopedSettings for - a global default value (that can be mutable), integration with Preferences and environment-variables, but behavior like a ScopedValue once a local scope is entered.

1 Like

Hmm, the semantics are a little different than what I was thinking of but it might serve my needs. How does it differ from the approach described by @disberd and @vchuravy (using a mutable RefValue as a ScopedValue)?