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

function declarations in local scope have inconsistent behaviour #34910

Open
cstjean opened this issue Feb 28, 2020 · 10 comments
Open

function declarations in local scope have inconsistent behaviour #34910

cstjean opened this issue Feb 28, 2020 · 10 comments
Assignees
Labels
kind:bug Indicates an unexpected problem or unintended behavior

Comments

@cstjean
Copy link
Contributor

cstjean commented Feb 28, 2020

This code fails on Julia 1.3:

julia> try 
           function bar end
           g(::typeof(bar)) = 934
           bar(a) = a+2
       finally end
ERROR: UndefVarError: bar not defined
Stacktrace:
 [1] top-level scope at REPL[3]:3

However, commenting either the second or third line of the try block makes it succeed. It seems to me that either function bar end should be an error outside of the top-level, or the above code should work... FWIW it came about because I have a macro that works at the top-level, but fails inside a @testset. I can pull it out, but it made sense to have it inside.

@cstjean cstjean changed the title function declarations inside try blocks have inconsistent behaviour function declarations inside local scope have inconsistent behaviour Feb 28, 2020
@cstjean cstjean changed the title function declarations inside local scope have inconsistent behaviour function declarations in local scope have inconsistent behaviour Feb 28, 2020
@JeffBezanson JeffBezanson self-assigned this Feb 28, 2020
@brenhinkeller
Copy link
Sponsor Contributor

Not sure what the best label for this is, but does still reproduce in 1.8.3 and 1.9.0-alpha1

@cstjean
Copy link
Contributor Author

cstjean commented Nov 20, 2022

Yes, still the same behaviour on 1.8. It's very much a corner case. I don't know what the outcome should be TBH...

@LilithHafner LilithHafner added the kind:bug Indicates an unexpected problem or unintended behavior label Dec 4, 2022
@LilithHafner
Copy link
Member

This is even worse with conditional function declarations

julia> (let
           if true
               f() = 1
           else
               f() = 2
           end
       end)()
WARNING: Method definition f() in module Main at REPL[66]:3 overwritten at REPL[66]:5.
2

julia> (let
           if false
               f() = 1
           else
               f() = 2
           end
       end)()
WARNING: Method definition f() in module Main at REPL[67]:3 overwritten at REPL[67]:5.
ERROR: UndefVarError: `f` not defined
Stacktrace:
 [1] top-level scope
   @ REPL[67]:5

from #5148 (comment)

@elextr
Copy link

elextr commented Dec 4, 2022

For the first conditional case, IIUC if does not create a scope, so doesn't that mean the two definitions are both actually in the let scope hence the "overwritten" message?

@LilithHafner
Copy link
Member

They are in the same scope, but code in the else branch of an if true statement should not be executed. For example, at the toplevel, we have this more intuitive behavior:

julia> if true
           f() = 1
       else
           f() = 2
       end
f (generic function with 1 method)

julia> f()
1

@elextr
Copy link

elextr commented Dec 4, 2022

On thinking about it some more, and based on my understanding as expressed on #47733 declarations happen irrespective of execution, a declaration after a return still exists throughout the scope even though its never executed. So that applies to function declarations/method definitions as well.

So the if is not controlling the declaration of the function f() at line 3 or the redefinition of the method at line 5, they are not executable statements and you get a redefinition message in both if true and if false cases.

But what the if is determining is the return value, and in the true that is a function object that was declared there, and when called the redefined method is used and returns 2. In the false clause its a method redefinition. I am not sure what that means in terms of the object returned, but it does seem to confuse things.

@elextr
Copy link

elextr commented Dec 4, 2022

Our posts overlapped, but I understand that the rules of REPL globals are different because its not really a "scope".

@uniment
Copy link

uniment commented Dec 4, 2022

Also from #5148 (comment):

julia> function fn_generator3()
                  if rand(Bool)
                      f() = 0
                  else
                      f(x, y) = x+y
                  end
              end
fn_generator3 (generic function with 1 method)

julia> fn_generator3()
(::var"#f#4") (generic function with 2 methods)

julia> fn_generator3()
ERROR: UndefVarError: f not defined

julia> fn_generator3()
(::var"#f#4") (generic function with 2 methods)

and

julia> i=3
3

julia> function f1()
           i=5; g() = 1
           for i=1:3
               g() = @show(i); g() # ok so we've defined `i` at local scope, parent scope, and global
           end
           g
       end
WARNING: Method definition g() in module Main at REPL[126]:2 overwritten at REPL[126]:4.
f1 (generic function with 1 method)

julia> f1()() # where is `i` *not* defined!?
ERROR: UndefVarError: i not defined

As suggested in the #5148 comment, I think the solution is to throw errors on const and named function declarations in conditionally-evaluated code in local scopes (unless nested in an even more-local scope than the conditional), and for any const or named function declaration in a local scope to always be at the most-local scope, i.e. any local const should behave like local const, local g()=... should behave like local g()=..., and local function f end should behave like local function f end. Any desire to capture and manipulate an identifier at parent scope can be achieved anyway with anonymous functions.

@uniment
Copy link

uniment commented Dec 5, 2022

I just realized that local function f end causes a malformed expression syntax error, and I have no idea why that's the case when local function f() end is fine 😅

The OP case seems to do with declaring local functions that type-specialize on locally-defined types. For example:

julia> b=try
           bar = "Hello, world!"
           h = (::typeof(bar)) -> 934
       finally end
ERROR: UndefVarError: `bar` not defined

@uniment
Copy link

uniment commented Dec 5, 2022

Meanwhile:

julia> function h end
h (generic function with 0 methods)

julia> b=try
           bar = "hello, world!"
           h(::typeof(bar)) = 934
       finally end
h (generic function with 1 method)

julia> h("hi")
934

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug Indicates an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

6 participants