On v1.0.2 I used a return statement from within a let block that got assigned to a variable. This didn't work as I expected, but it also didn't cause any errors. The surprising side effect was that the variable assigned to was never actually created.
julia> x = let
# some ugly calc
42
end
42
julia> x
42
julia> y = let
# some ugly calc
return 42
end
42
julia> y
ERROR: UndefVarError: y not defined
return doesn't return from the enclosing block, it returns from the enclosing function. Here there is no clear function, so this perhaps should be disallowed, but it happens to return from the entire top-level expression. So the whole input finishes before the assignment can happen, and y is never assigned.
👍 to an error. It looks like this is a lowering thing?
julia> Meta.@lower y = let
# some ugly calc
return 42
end
42
julia> Meta.@lower y = let
# some ugly calc
return 42
32
end
:($(Expr(:thunk, CodeInfo(
1 ─ return 42
2 ─ y = 32
└── return 32
))))
Thinking this over, I'm not sure disallowing return at the top level is the right thing. It might be useful for copy-pasting code from a function to the REPL. It's also a bit strange to have a way to exit an entire function body early, but no way to exit an entire top-level expression early.
It's also a bit strange to have a way to exit an entire function body early, but no way to exit an entire top-level expression early.
That was how I had hoped to use the syntax... Another side effect from this issue is that the variable name seems to be created in a module when using const, but not with no const.
julia> module Mod
x = let
return 42
end
const y = let
return 42
end
end
Main.Mod
julia> names(Mod, all=true)
6-element Array{Symbol,1}:
Symbol("#eval")
Symbol("#include")
:Mod
:eval
:include
:y
julia> Mod.y
ERROR: UndefVarError: y not defined
Allowing return, continue, break, etc. in the REPL is extremely handy for cut-and-paste debugging. IMO, return outside of a function and continue and break outside of a loop should all just stop evaluating the top-level expression immediately. We could be stricter in other top-level contexts such as inside modules or in non-interactive scripts, but I've found having to edit pasted code to remove continue and break to be a huge hassle and would hate to see more of the same.
Let's just document that return also exits top-level expressions. We can separately decide what to do with break and continue at the top level.
From discussion on triage, in older Julias the actual behavior was that if you didn't evaluate the continue or break then there was no error, whereas later it became a static error which means that anything earlier in the expression isn't evaluated, which is where the annoyance comes in. What might be good is to go back to the older behavior where it's only an error if the continue or break are actually evaluated. Not sure where return would fall into that; I kind of like the way return works.
Most helpful comment
👍 to an error. It looks like this is a lowering thing?