Julia: disallow `return` outside function?

Created on 23 Dec 2018  ·  7Comments  ·  Source: JuliaLang/julia

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
doc

Most helpful comment

👍 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
))))

All 7 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ararslan picture ararslan  ·  3Comments

musm picture musm  ·  3Comments

yurivish picture yurivish  ·  3Comments

StefanKarpinski picture StefanKarpinski  ·  3Comments

omus picture omus  ·  3Comments