Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

On a one-time self-re-defining function pattern

Consider the following pattern:

function foo /* aka outer_foo */ () {

    // === stage 1 ===
    // declaration and initialization of closure variables

    var ... <CLOSURE_VARS> ...;

    // initialization of CLOSURE_VARS
    // (possibly expensive and/or depending on values known only at run-time)


    // === stage 2 ===
    // one-time redefinition of foo

    foo = function /* inner_foo */ ( arg0, ... ) {
        var results;

        // code computing value of results based on CLOSURE_VARS, arg0, ...

        // foo is never modified here

        return results;
    };


    // === stage 3 ===
    // invocation of inner function and returning of result

    return foo.apply( this, arguments );
}

For convenience, I will use the terms outer_foo and inner_foo to designate, respectively, the outer and inner functions above, even though the code does not use these identifiers.

The body of outer_foo comprises three stages (the last two consisting of a single statement each):

  1. declaration and initialization of some closure variables;
  2. redefinition of the identifier foo;
  3. passing to inner_foo the arguments originally passed to outer_foo, and returning the result.

The body of outer_foo will be executed at most once, namely, the first time that "the function named foo" is called, if it ever happens. Henceforth, the body of outer_foo will be unreachable, and all subsequent calls to "the function named foo" will result in the execution of inner_foo.

In general, one may envision some variations on this pattern 1, but the essential constraints on the basic scheme I'm talking about here are:

  1. foo is re-defined exactly once during the execution of outer_foo2;
  2. foo is never re-defined during the execution of inner_foo.

(If these essential constraints are violated, all bets are off; such a case is outside the scope of this question.)


I am already aware of at least one downside of this scheme: some practitioners consider self-redefining functions confusing, detrimental to code readability, and/or inherently in poor taste, even when the re-definition happens only once in a completely deterministic manner, as is the case in the scheme above.

My question is:

Does this scheme have any additional downsides, and if so, what are they?

I'm particularly interested in those downsides that are specific to JavaScript.


1 For example,

function foo /* aka outer_foo */ () {
    var inner_foo, ... <CLOSURE_VARS> ...;

    if ( some_deterministic_test() ) {
        inner_foo = function ( arg0, ... ) {
            // etc.
        }
    }
    else {
        inner_foo = function ( arg0, ... ) {
            // etc.
        }
    }


    foo = inner_foo;

    return foo.apply( this, arguments );
}

In this variant, the function ultimately assigned to foo depends on some test performed at run-time.

2 Here it's tempting to stipulate that foo not only must be re-defined exactly once within outer_foo, but that this be done "deterministically". Of course, any deviation from "determinism" (however one chooses to define it) in final setting of foo can only increase the complexity of the code's run-time behavior. Unfortunately, I don't know how to make this stipulation precise without descending into a maze of lawyerly minutiae. The best I could do would be to add the weasely—and borderline incoherent—phrase "the more deterministic the better", and hope that readers get my point. The only utility of this additional stipulation, however, would be to rule out perverse, but wholly unrealistic, scenarios (e.g. having the final value of foo depend on the outcome of a random process), so I left it out.

like image 955
kjo Avatar asked Jan 20 '26 02:01

kjo


1 Answers

I can think of two additional points you should be aware of:

  • performance. Compilers are like the people you mentioned. Some of them will get confused by this pattern, and they might not be able to optimise it like they would if a different pattern was used. This might or might not be a concern, but you should make sure to test it.
  • broken contracts. You say you expect outer_foo to be executed at most once. This might not be the case! Someone might pass it around and put a reference to it somewhere, so that it does not become unreachable when being called. Sure, that's unlikely, but depending on your requirements you might want to guard against it.
    After all, it's kind of a broken design, as a mere alias (var f = foo; f()) should never alter functionality. Make sure all consumers/users know what you are doing so they don't trip over it.
like image 51
Bergi Avatar answered Jan 21 '26 16:01

Bergi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!