In certain contexts we refer to rvalues and lvalues, but in general (and this is how the Julia documentation uses these words) lvalues are called variables, and rvalues are called values.
Values (rvalues) in Julia have types. This is true of any language higher-level than Forth. It is also possible to give lvalues types, but of course we agree that the lvalues are dynamic, since without annotation, you can change both the value and type of a variable. I could quibble that “really” it’s a default top type of Any
, but Julia refers to itself as a dynamic language and I see it that way also. This doesn’t seem like the place for a philosophical digression about the taxonomy of a language with a fully-integrated gradual typing system, but the fact that type errors occur at runtime is fairly conclusive, if we’re using a binary of static or dynamic, imho.
The rvalues are not dynamic. Some are mutable, some immutable, but without resorting to reinterpret
they are of a single static type for their lifetime.
ScopedValues give you a value (rather than a variable) which is dynamic, specifically, dynamic in scope. Observe:
julia> a = ScopedValue(1)
ScopedValue{Int64}(1)
julia> b = a
ScopedValue{Int64}(1)
julia> with(a => 3) do; println(b) end
ScopedValue{Int64}(3)
This is not a static value, it is a dynamic value. I would argue, as long as we’re doing language taxonomy, that this is true of Refs as well. But they aren’t scoped.
Which is why, despite having spun off a whole thread to kibbitz about it, I don’t especially care that ScopedValues
was chosen for the name here. I hadn’t booted up nightly to play with the implementation, and after doing so I would even say that ScopedRef
might be the best choice, that’s pretty much what they are. I had assumed before reading the manual that access would look like a
not a[]
, which feels less like a classic dynamic variable. So be it.
It’s a decent feature to ship, easy to implement, minimal changes to the language itself. That does leave some rough edges, as the manual warns “Scoped values are constant throughout a scope, but you can store mutable state in a scoped value. Just keep in mind that the usual caveats for global variables apply in the context of concurrent programming.” This implies to me that if you change the binding of a ScopedValue with a with
closure, and some other Task in, ahem, a different scope, happens to read it while the other thread is inside the closure, it will get the inner value, not the outer one. Which is unsatisfactory, and fights against the “scoped” part of the concept (not talking about the name for once!).
I’m not convinced that first-class dynamic values (or dynamic variables, as they’re called in most cases) are worth the language complexity they would bring. Those would work like this:
dyn = Dynamic(0)
function closed(i)
global dyn + i
end
Threads.@thread for i = 1:10
let global dyn = rand(1,10) # This is thread-specific
println(closed(i))
end
end
The let-bound value of dyn (which is accessed without []
) would be correctly bound by the scope, in each thread, to the result of rand
, within closed
. Because this hypothetical construct uses assignment to rebind the value, a const
dynamic wouldn’t allow rebinding (and its dynamic nature would therefore be wasted). Although really the interaction with const
and typeasserts could be whatever the implementation wanted, because this would have be baked in on a fairly deep level. There are several subtle questions I’m not even going to try to answer here.
Making this work would call for far-reaching changes to the compiler, and I am not at all convinced that anything like this is worth having. If (I haven’t confirmed this) the ScopedValue
s implementation isn’t thread-safe, that would be a good thing to change.
I’d really like to wind this down because I’m sure it gives the impression that I care a lot more about the name in question than I actually do. I want to conclude by pointing out that my issue with ScopedValues is that they do not use the scoping mechanism, rather than the truly minor quibble that the dynamic part of the term “dynamic scope” should have been chosen. The reason that matters is that what’s been implemented here is the dynamic bind part of how dynamic variables work in Lisps. Not the scope part.
If they can manage to make them thread safe, I will no longer care at all. Show me a language which has no weird or questionable names for elements of the language. You can’t.