After months of on and off work, Porffor finally has closures implemented. If you don't know, closures are a fancy way of binding variables across lexical boundaries. It means you can do this:
function foo() {
let cool = 1337;
return () => cool++;
}
const bar = foo();
bar(); // 1337
bar(); // 1338...without getting ReferenceError: cool is not defined. Since from a compiler view, cool is not defined inside bar. After compile-time semantic analysis, we figure out bar needs cool from a closure capture. When instancing bar, we capture its environment { cool }. Then we pass that environment for every call to it. For me at least, it sounded relatively simple to do. It is not simple, while maintaining performance at least.
There is an essential tradeoff: you want as little closure captures as possible without breaking things. After some work and testing it is somewhat easy to nail this but fun scenarios like loops which need to be instanced on every iteration and self referencing can easily trip things up. I won't go into detail here as it is mostly just having semantic analysis capture everything, then refining to only capture what is needed without regressing.
Not only is capturing variables wasting CPU cycles, it wastes memory too. I have plans in the future to add compiler warnings/linting to warn you when code is slow and could likely be faster, closures will likely be a big part of it. If you are wondering why closures are happening now, stay tuned for the next post ;)