Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider renaming ScopedValue to ContextValue #53644

Closed
CameronBieganek opened this issue Mar 7, 2024 · 6 comments
Closed

Consider renaming ScopedValue to ContextValue #53644

CameronBieganek opened this issue Mar 7, 2024 · 6 comments

Comments

@CameronBieganek
Copy link
Contributor

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:

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.

@CameronBieganek
Copy link
Contributor Author

We could potentially expose a somewhat lower-level interface, like this:

f() = a[]
a = ContextValue(1)

ctx = current_context()

with(ctx, a => 2) do
    f()
end

Just a thought. I don't know if that is viable or not.

@vchuravy
Copy link
Sponsor Member

vchuravy commented Mar 7, 2024

The name was extensively debated during triage (We spent 2-3 meetings on it) and on slack.

While on the surface ScopedValue seem similar to contextvar in Python it has a subtle but very important difference. The value can not change during a scope, only by entering a new dynamic scope the value can be changed. Without this restriction the compiler optimizations could not be implemented.

Below are a couple examples that I think emphasize that what ScopedValue provides is not dynamic scoping.

That is a fundamental misunderstanding of the terms variable and values. Julia does not (an likely never will) implement dynamically scoped variables. Variables only have lexical scope to consider.

ScopedValue implements a container type whole value is constant within a scope, said scope is called dynamic since it depends on the execution of the program.

@CameronBieganek
Copy link
Contributor Author

@KristofferC Maybe allow some discussion rather than immediately closing? That's a bit rude and discourages feedback and contributions to the language. I sometimes wonder why I involve myself with this language.

While on the surface ScopedValue seem similar to contextvar in Python it has a subtle but very important difference. The value can not change during a scope, only by entering a new dynamic scope the value can be changed. Without this restriction the compiler optimizations could not be implemented.

I don't see how this relates to whether this feature can be described as "dynamic scoping".

That is a fundamental misunderstanding of the terms variable and values. Julia does not (an likely never will) implement dynamically scoped variables. Variables only have lexical scope to consider.

I understand what variables and values are. The concept of dynamic scoping relates to the binding of variables. Your argument might be more convincing if the documentation was careful to use phrases like "dynamically scoped value", rather than claiming that Scoped Values are "an implementation of dynamic scoping in Julia". But as it currently stands, the messaging in the docs is misleading. Here is a list of misleading statements in the manual page:

  1. The very first sentence: "Scoped values provide an implementation of dynamic scoping in Julia."
    • ScopedValue provides dynamically scoped values. It does not provide an implementation of dynamic scoping, which would require dynamically scoped variables.
  2. "Lexical scoping is the default behavior in Julia."
    • The word "default" implies that Julia does not always use lexical scoping, which is incorrect.
  3. "Under dynamic scoping a variable is bound to the most recent assigned value during the program's execution."
    • This statement is correct, but note that it refers to the binding of a variable, whereas ScopedValue is a dynamically scoped value.
  4. "Now using a ScopedValue we can use dynamic scoping."
  5. "Dynamic scopes are inherited by Tasks ... In the example below we open a new dynamic scope before launching a task."
  6. The ScopedValue docstring: "Create a container that propagates values across dynamic scopes. Use with to create and enter a new dynamic scope."
    • This is a rather confusing mix of concepts. Dynamic scopes are concerned with the bindings of variables, yet here we are talking about propagating values.
  7. Various other casual references to "dynamic scope" throughout the manual page.

P.S. It would be nice if core developers refrained from using phrases like "That is a fundamental misunderstanding of...". It's remarkable how often I hear that, and usually it's an incorrect assertion. It comes across as condescending and unwelcoming.

@vchuravy
Copy link
Sponsor Member

vchuravy commented Mar 7, 2024

P.S. It would be nice if core developers refrained from using phrases like "That is a fundamental misunderstanding of...". It's remarkable how often I hear that, and usually it's an incorrect assertion. It comes across as condescending and unwelcoming.

My apologies, this particular bike-shed is very frustrating for me. I appreciate your comments and thoughts, but I have also debated this particular question for many hours of my life. The examples you showed were for variables not values, and ScopedValue's very intentionally say nothing about variables and only about the behavior of values.

It does not provide an implementation of dynamic scoping, which would require dynamically scoped variables.

I suspect this is where we disagree, for me "dynamic scoping" does not immediately imply "variables".
A lot of the inspiration for me came from https://openjdk.org/jeps/446, in particular see the section on "The meaning of "scoped""

I do deeply appreciate your documentation PR, clarifying things that might be obvious to me but not others is very important.

There are also costs associated with renaming at this stage of the Julia 1.11 release process. (I suspect this is why Kristoffer closed the issue).

I didn't pull the name out of thin air and we debated it at length. I asked the Java folks how they ended up with the name:
Quoting from the email (lightly edited to remove names).

[...]
For a little while we had the idea of a scope-local variable, like a
LISP special variable, but we decided to favour immutability. Then we
tried a "special" version of a ThreadLocal, but that was fugly as
anything so it had to die. Apart from anything else, it violated the
Liskov Substitution Principle. We had a lot of trouble with
inheritance, too, and the eventual highly-restricted model of
inheritance was the result.

So, we went through many designed and, as a consequence of that, many
names.

I objected to "scoped variable" as an idea strongly because it's not a
variable, it's a bound value. The name "ExtentLocal" came up because
"scope" is already defined in the JLS to mean a lexical scope in a
program, whereas this is a dynamic scope. We defined "extent" to mean
what is commonly known as a "dynamic scope" in an attempt to get path
that problem. [...]

Eventually, to try to break the logjam, we had one of the most amazing
meetings I've ever attended, lasting one hour.
All that we discussed was the name of this class.

After a great deal of back-and-forth we converged on ScopedValue,
which I suggested and others endorsed. The adjectival form
"scoped" suggests limited in extent, and indeed the OED gives "extent"
as a near-synonym of "scope". So it's good enough, I think.

@CameronBieganek
Copy link
Contributor Author

@vchuravy Thanks for the background info.

@KristofferC
Copy link
Sponsor Member

Maybe allow some discussion rather than immediately closing? That's a bit rude and discourages feedback and contributions to the language.

Closing does not mean that there is no discussion allowed. As can be seen here, the discussion keeps going. (There is also https://discourse.julialang.org/t/naming-of-scopedvalues/110747 so it is a bit unfortunate to split the discussion into two places)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants