1
votes

Consider the following functions whose definitions contain re-definitions of functions.

function foo1()
    x = 1
    if x != 1
        error("Wrong")
    end
    x = 2
end

function foo()
    function p(t) return t + 1 end
    if p(1) != 2
        error("Wrong")
    end
    function p(t) return 1 end
end

foo1() runs without error, but foo() gives the error Wrong. I thought it may have something to do with Julia not supporting redefining functions in general, but I'm not sure. Why is this happening?

1

1 Answers

3
votes

I would say that this falls into a more general known problem https://github.com/JuliaLang/julia/issues/15602.

In your case consider a simpler function:

function f()
    p() = "before"
    println(p())
    p() = "after"
    nothing
end

Calling f() will print "after".

In your case you can inspect what is going on with foo in the following way:

julia> @code_typed foo()
CodeInfo(
4 1 ─     invoke Main.error("Wrong"::String)::Union{}                                                                                │
  │       $(Expr(:unreachable))::Union{}                                                                                             │
  └──     $(Expr(:unreachable))::Union{}                                                                                             │
) => Union{}

and you see that Julia optimizes out all internal logic and just calls error.

If you inspect it one step earlier you can see:

julia> @code_lowered foo()
CodeInfo(
2 1 ─      p = %new(Main.:(#p#7))                                                                                                    │
3 │   %2 = (p)(1)                                                                                                                    │
  │   %3 = %2 != 2                                                                                                                   │
  └──      goto #3 if not %3                                                                                                         │
4 2 ─      (Main.error)("Wrong")                                                                                                     │
6 3 ─      return p                                                                                                                  │
)

any you see that p in top line is assigned only once. Actually the second definition is used (which is not visible here, but could be seen above).

To solve your problem use anonymous functions like this:

function foo2()
    p = t -> t + 1
    if p(1) != 2
        error("Wrong")
    end
    p = t -> 1
end

and all will work as expected. The limitation of this approach is that you do not get multiple dispatch on name p (it is bound to a concrete anonymous function, but I guess you do not need multiple dispatch in your example).