Я бы сказал, что это относится к более общеизвестной проблеме https://github.com/JuliaLang/julia/issues/15602.
В вашем случае рассмотрим более простую функцию:
function f()
p() = "before"
println(p())
p() = "after"
nothing
end
Вызов f()
выведет "after"
.
В вашем случае вы можете проверить, что происходит с foo
следующим образом:
julia> @code_typed foo()
CodeInfo(
4 1 ─ invoke Main.error("Wrong"::String)::Union{} │
│ $(Expr(:unreachable))::Union{} │
└── $(Expr(:unreachable))::Union{} │
) => Union{}
и вы увидите, что Джулия оптимизирует всю внутреннюю логику и просто вызывает error
.
Если вы проверите его на один шаг раньше, вы увидите:
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 │
)
любое, что вы увидите, что p
в верхней строке назначается только один раз.На самом деле используется второе определение (которое здесь не видно, но можно увидеть выше).
Чтобы решить вашу проблему, используйте анонимные функции, такие как:
function foo2()
p = t -> t + 1
if p(1) != 2
error("Wrong")
end
p = t -> 1
end
и все будут работать какожидается.Ограничением этого подхода является то, что вы не получаете многократную диспетчеризацию для имени p
(это связано с конкретной анонимной функцией, но я полагаю, вам не нужно многократная диспетчеризация в вашем примере).