Hacker News new | ask | show | jobs
by gsg 5282 days ago
If you have a function that returns a function, basically you need to rewrite things to lift the referenced state into the outer scope. In pseudo-C:

    (void -> int) incrementer(int initial_value) {
	int count = initial_value;
	return lambda() { return count++; }
    }
might be rewritten as:

    struct hidden_environment_type {
	int count;
    }

    int incrementer_lambda(hidden_environment_type *env) {
	return env->count++;
    }

    void incrementer(hidden_environment_type *hidden_arg, int initial_value) {
	hidden_arg->count = initial_value;
    }
Now the caller will allocate a variable of type hidden_environment_type, and pass its address as the first (artificial) argument.

Note that lambda functions require a different calling convention to C function pointers, since they need to be passed a pointer to their environment in addition to their regular arguments. Introducing lambdas either requires some resolution of this complication, such as specialisation machinery (ie in C++11, templates provide the necessary specialisation). Another option is to use the environment-passing convention for every indirect call, possibly giving a minor performance hit for calling raw function pointers.

Another issue is that the size of an environment type depends on the code of a function involving it, so lambdas don't have uniform size unless some additional indirection is introduced, probably to heap storage (Apple's blocks extension to C takes this approach). This makes it either impossible or slightly more expensive to store lambda functions in data structures, depending on which implementation you choose.

After all this, your ownership/lifetime question boils down to "who is holding onto the instance of hidden_environment_type?", which is a pretty simple question to answer in C-like languages.