Function definition inside let-block impacting performance?

As Mason showed, macros can perform something like lowering (not exactly, we certainly don’t write so many GOTOs). If a macro over a local scope can be proved to apply all the time, then it’s something to put on a backburner for when the lowerer can be improved.

Type inference is the job of the compiler, and by the language’s design, the compiler only knows types from a call signature. The lowerer at the method definition does not have access to a call signature, so it can never infer types like the compiler does later.

Not true, if the lowerer can see that a was only assigned once prior and never again, then it’ll lift the closure’s type to include a type parameter a::T. When the compiler comes in, it’ll compute 0.5 from the literals and instantiate the closure with T == Float64. No heap-allocated box needed.

I’d say it is, but if we take a step back and look, we’re essentially lifting the method and giving it 1 type for efficiency in the global scope, so if we don’t really need to capture any variables from a local scope like the fib example, we could just do all of this in the global scope. If we just don’t want to expose it, then we could use weird variable names and in an upcoming version just not declare it public.

This part is just a genuine difficulty of inferring captured variables that get reassigned and general impossibility of narrowing down the type. Other languages deal with this by declaring the type of the captured variable, even generically; this also works in Julia, though the value is still heap-allocated for memory safety. You can take the allocation out of the compiler’s hands by implementing a shared value as a mutable value rather than a reassigned variable, and you can pass such a value into many scopes. The only possible inference improvements are for very limited yet useful treatment of captured variables, like inferring all reassignments before closures or inferring lines in yet-to-be-called closures that only involve the captured variables. The other usage limitations are there because of mechanisms for good performance, and it’s more likely they’ll get better warnings and errors; there’s a reason that languages with more dynamic features are slow.

Doesn’t seem true, an optimization of Lua upvalues (which refers to the variable, not an instance) can leverage their immutability (not being reassigned), which happens in Julia already. If the variables are reassigned, then the upvalue must be allocated, which is Core.Box in Julia. I don’t know if any implementations of Lua or its derivatives, particularly LuaJIT, are any better at inferring the undeclared type of a captured and reassigned variable.

1 Like