Open
Description
I decide to start discussion about simple closure implementations.
Some obvious (and may be naive) implementation is using generation class context which I prefer to demonstrate in following example:
declare function externalCall(a: i32, b: i32): void;
function range(a: i32, b: i32, fn: (n: i32) => void): void {
if (a < b) {
fn(a);
range(a + 1, b, fn);
}
}
export function test(n: i32): void {
range(0, n, (i: i32) => {
externalCall(i, n); // capture n
});
}
which transform to:
// generated
class ClosureContext {
fn: (ctx: usize, i: i32) => void;
n: i32; // captured var
// optinal "self: usize;" is closure instantiate inside instance class method
parent: ClosureContext | null = null;
}
// generated
function lambdaFn(ctx: usize, i: i32): void {
externalCall(i, changetype<ClosureContext>(ctx).n);
}
function range(a: i32, b: i32, ctx: usize): void {
if (a < b) {
changetype<ClosureContext>(ctx).fn(ctx, a); // replaced from "fn(a)";
range(a + 1, b, ctx);
}
}
export function test(n: i32): void {
// insert
let ctx = new ClosureContext();
ctx.fn = lambdaFn;
ctx.n = n;
//
range(0, n, changetype<usize>(ctx));
}
Closure and ClosureContext will not generated when no one variable was captured and use usual anonym functions.
ClosureContext will not created when only this
was captured. In this case ctx
param use to pass this reference.
Other discussions: #563
@dcodeIO @jtenner @willemneal Let me know what you think about this?