Using local
in the global scope does not error but does not do anything either:
julia> local x = 10
10
julia> x
ERROR: x not defined
julia> module A
local x = 10
y = x+ 10
end
ERROR: x not defined
I would think that either throwing an error on local x = 10
or just ignoring the local
would be the right thing to do.
Fix was reverted in faae1942eed74eb486c32eeae4b6ea8e5ef63561.
Related, this local
statement should also error:
julia> begin
local x = 1
y = x+1
end
2
julia> x
ERROR: UndefVarError: x not defined
julia> y
2
or if not then it should error on y=x+1
as begin
blocks should not influence scope.
This came up on julia-users, below is a minimal test case. I think this is related, if not I can make a new issue. Note that it errors in 0.5 and works in 0.4. (If local
were to error in begin-end
blocks this should error, but differently.)
~/julia/issues >> julia5
_
_ _ _(_)_ | A fresh approach to technical computing
(_) | (_) (_) | Documentation: http://docs.julialang.org
_ _ _| |_ __ _ | Type "?help" for help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 0.5.0-dev+2650 (2016-02-13 20:03 UTC)
_/ |\__'_|_|_|\__'_| | Commit efc2efb* (11 days old master)
|__/ | x86_64-unknown-linux-gnu
julia> s = 0;
julia> begin
local x = 1
local val = for i=1:2
s = x+s
end
end
ERROR: UndefVarError: s not defined
[inlined code] from ./none:4
in anonymous at ./no file:4294967295
in eval(::Module, ::Any) at ./boot.jl:267
julia>
~/julia/issues >> julia
_
_ _ _(_)_ | A fresh approach to technical computing
(_) | (_) (_) | Documentation: http://docs.julialang.org
_ _ _| |_ __ _ | Type "?help" for help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 0.4.3 (2016-01-12 21:37 UTC)
_/ |\__'_|_|_|\__'_| |
|__/ | x86_64-unknown-linux-gnu
julia> s = 0;
julia> begin
local x = 1
local val = for i=1:2
s = x+s
end
end
julia>
Complication: we like to be able to paste code from functions into the REPL if possible, and disallowing local
in global scope would make this possible less often.
Ok, that would be a +1 for "just ignoring it". (Note though, because of hard & soft scope, copy-paste does not work always anyway.)
Looking back at my previous attempt to fix this, the real wrinkle is macros. A macro like @time
needs to introduce new unique variables into the current scope. We don't want those to be global variables, since that would litter the global namespace with the macro's temporary variables. Nor can the macro wrap its output in let
, since that would change the scope of the user's expression. So the only solution we currently have is to allow top-level expressions to contain a mix of local and global variables. In local scope this is not a problem, since "littering" a local scope with new temp variables is harmless.
The other possible solution would involve macro hygiene. Basically we'd want to implicitly wrap the result of a macro in something like a let
block, but that only local-izes its own hygienic variables.
So the only solution we currently have is to allow global
begin ... end blocks
to contain a mix of local and global variables.
So begin ... end
does impact scope after all. Maybe this should be documented?
What really happens is that if a top-level expression contains local x = ...
, then that expression has its own local scope. I shouldn't have mentioned begin ... end
blocks there, since that's not what triggers this behavior. As in your OP, local x = 10
by itself does the same thing, as would e.g. if y; local x = 10; end
. So it's not a feature of begin
itself.
Technically, changing this would be breaking, but it's a kind of weird thing to do. Should we just document local
in global scope as having undefined behavior and then move this off the milestone?
I propose removing this from the milestone. Nice to fully work out at some point, but doesn't seem crucial.
Wouldn't it be fairly simple to just raise an error for local
in global scope? Or is there some deeper complication that I'm missing here?
It's currently useful for macros to expand to top-level expressions that contain local
.
Ah, I see. Changing this does not actually seem at all pressing to me. I'd be fine with leaving this as-is with the justification that it makes macro stuff easier. What's the drawback of this feature?
I don't see any major problems, it's just weird.
Whatever, let's just keep it then. I guess we can document that it may go away and then see how much stuff in the ecosystem breaks if we ever decide to change it and let that be the deciding factor on whether it's a 1.x change or a 2.0 change.
Since it came up in two discussions today, I am wondering if this behavior could just be kept as it is, and documented in the docstring ?local
as intentional:
If there is agreement on this, I would be happy to do a PR to the docstring.
Note, however, that if you have local
in a function body and cut-and-paste it, that generally won't work right.
Most helpful comment
Complication: we like to be able to paste code from functions into the REPL if possible, and disallowing
local
in global scope would make this possible less often.