> Why do you think Julia was made for JIT from the beginning?
It's very explicit in almost anything the language designers wrote from the beginning. Julia has always had it's language semantics designed around a JIT. Keep in mind, julia's JIT is not at all like JS JITs, julia's style is often called 'just ahead of time'.
For the you quote, the only thing causing a problem is the global y. Using global variables is very frowned upon in julia for this reason. However, if you write
function g(y)
function f(x)
x + y
end
end
or something, then there is no `Any` involved at runtime. What will happen is that say you call g(1.0)(2), as soon as julia sees this, it will do static type inference on the function bodies, knowing that y :: Float64 and x :: Int, all the code can be inferred and compiled down. Julia specializes on all arguments of functions, whether or not you put a type assertion on them.
If there's a point in the program where type inference fails to produce concrete types, then the compiler just generates code up to that point and then waits until the types are resolved at runtime, and then using the new runtime type information does static analysis going forward from that point until it either encounters another type instability, or the program is finished.
This is why writing code that isn't concretely inferrable can carry a heavy performance penalty in julia, but it's also why inferrable code can be so fast. The trick is just to keep type dynamism away from performance bottlenecks and you'll be fine.