IIFE Statements & Expressions

Like many people, I frequently use IIFEs, but hate the way they look (especially what Crockford calls "dog balls"):

(function animate() {
    requestAnimationFrame(animate);
    graphics.render();
})();

This can be slightly improved using void (though people often use a bang instead):

void function animate() {
    requestAnimationFrame(animate);
    graphics.render();
}();

I normally define a helper named iife that just takes a function and calls it, so I can do this kind of thing:

iife(function animate() {
    requestAnimationFrame(animate);
    graphics.render();
});

This works fairly well, except that it's still a bit noisy, and I now have to define (or import) this weird, little helper function at the top of half of my modules.

We could plausibly standardize the iife function and make it global, but I'd like to propose something else.

A generalized invocation operator, like the do operator from CoffeeScript, would be a bridge too far. It was often misused, so you'd regularly see CoffeeScript code like this:

gain = do audioContext.createGain

That said, a more limited do-function grammar that must define the function that is being immediately invoked would be very nice to have. Something like this:

do function animate {
    requestAnimationFrame(animate);
    graphics.render();
}

Note: The function name (animate in the example) is (generally) optional, while the parens (around params) would always be omitted (as above), as they are redundant.

I'm not sure there's any need to support a do-class equivalent (I cannot think of a single usecase for an immediately invoked class expression, and want to avoid generalizing do). However, supporting immediately invoked async-functions and generators does seem helpful. The grammar would be obvious:

do async function { ... }
do function * { ... }

Arrow functions would work the same way:

const toggle = do => {
    const element = document.querySelector(selector);
    return (name) => element.classList.toggle(name);
};

Having a limited grammar also prevents confusion with the (existing, but generally avoided) do-while grammar and the (proposed, and frankly, horrible) do-expression grammar.

1 Like

If you don't like the dog balls, just format them differently, as Crockford prefers:

(function animate() {
    requestAnimationFrame(animate);
    graphics.render();
}());

But really, IIFEs are not that common any more as at the height of the module pattern and closure-in-loop problems, they're largely superseeded by module syntax and block scopes. There's hardly a need to introduce new syntax for this kind of function call. If you want to make the language simpler, more understandable and easier to read, use a function declarations and call the function:

function animate() {
    requestAnimationFrame(animate);
    graphics.render();
}
animate();
1 Like

If you want to make the language simpler, more understandable and easier to read, use a function declarations and call the function.

I don't like adding redundant variables to the local namespace, hence my preferred way of handling those situations:

iife(function animate() {
    requestAnimationFrame(animate);
    graphics.render();
});

I accept that this is subjective.

But really, IIFEs are not that common any more...

I personally use them a lot. I used to write CoffeeScript, where the syntax was really nice (for that kind of language):

animate = do ->
    requestAnimationFrame animate
    graphics.render()

I suspect JavaScript developers would use IIFEs more, if they were less clunky to express (which motivated proposing this suggestion), but I could just be wrong of course.

Your point about IIFEs being less relevant in modern JavaScript was interesting. Thanks for considering it, @bergus.

Variables are free, functions aren't, and more variables often makes code more clear, not less - so I'm not sure I'd find that preference reasonable personally.

imo IIFEs are fully obsolete in a world where modules exist, as well as TLA.

1 Like

Until we have do-expressions and/or pattern matching, though :( I've constantly found myself doing this:

const config = {
  locales: (() => {
    switch (process.env) {
      case "production": return ["en", "fa", "ja", "zh"];
      case "development": return ["zh"];
      case "test": return ["en"];
      default: throw new Error("Invalid env");
    }
  })(),
};

Without throw expressions, the above can't be refactored into a lookup table either. Also, immediately-invoked generator functions are nice to create ad-hoc iterators.

3 Likes

There are many examples of cases where a function needs to reference a variable that persists outside of the function, but is not referenced outside of the function. IIFEs permit the variable to be bound to those functions by closure:

const sfx = do function {
    const context = new AudioContext();
    return {
        bonk: () => {
            const oscillator = context.createOscillator();
            ...
        },
        chime: () => {
            const oscillator = context.createOscillator();
            ...
        }
    };
};

There are many examples of this kind of thing (and other cases, like @ljharb's example). Selecting and enclosing elements (allowing the functions that use them to avoid walking the DOM every time they're invoked) is quite common.

I'll disagree with this. I often try to minimize the number of variables I use as well (within reason). It's for this many languages allow us to create bare scope blocks (i.e. using a bare { ... }). However, IIFEs are more convenient than scope blocks, as they additionally allow us to "return" something from the scope and assign it to a variable that's outside of the scope.

So, for example, @7ombie's is possible using a bare scope block, but you'd have to declare the sfx variable using let, then assign to it once inside of the block. That's about as un-user-friendly as an IIFE IMO.

The convenient syntax that CoffeeScript provides for creating IIFEs is something I miss as well. It's probably the number 1 reason why I'm excited for the do-expression proposal - just so I can have scopes with "returns" - something that IIFE syntax would acomplish as well.

1 Like

I saw your alternative to the do-expression proposal (after starting this discussion). It's nice. It makes a lot of sense. This suggestion is effectively just an extension to what you suggested.

Syntactically, we'd be consistently using do <keyword> ... to create a bunch of new expression types that are each directly derived semantically from the statement that corresponds to whichever keyword is used (with specific do-keyword combinations to avoid generalization).

Generalization not only permits all sorts of wacky, valid code, but (as you mentioned in your proposal) it also implies wacky language features that require a list of awkward restrictions to eliminate.

I think you're on to something, whether or not support for do-function gets included.

I personally like the idea of loops evaluating to an array of each iteration's value, but JavaScript already dropped array comprehensions (after FireFox implemented them), and the same arguments will presumably apply to loop-expressions. I don't actually know what the arguments were.

The do-const thing is a bit flakey, but being able to use a block of code in an expression is really useful.

CoffeeScript had a lot of good ideas. Fun times.

One issue that's caused me to stop pursuing that idea, is that the pattern matching proposal really needs something to go with it that allows arbitrary statements to be executed in the expression position (or, rather, a statement-as-expression proposal would strongly complement pattern-matching - it's not required for pattern-matching to progress). Right now, "do expressions" is fulfilling that role. This allows you to do something like:

const result = match (something) {
  when (2): do {
    for (...) { ... }
    f(...)
  }
  when (3): do {
    for (...) { ... }
    g(...)
  }
}

I believe that whatever statement-as-expression proposal that goes in will need to fulfill this sort of use-case, and fulfill it well. My proposal really doesn't do a great job here. Ideally, when you move code into a match block, you shouldn't be required to do extensive refactoring on it to make it so it's composed purely out of expression-only operations.

IIFE syntax does a better job at handling this use case, but in a scenario like this, it would be nice if you could use "break" or "return" or whatever while you're inside the match construct.

I believe do blocks does the best job at handling this use case. Conceptually, the best fit for this job would be a block (not a function) that's in an expression position. Really, the only quibble I have with the do expression proposal as it currently stands, is that I don't like the implicit nature of how values get "returned" from the block, I'd rather have an explicit keyword (having an explicit keyword would also mean we don't have specific statements that are and aren't allowed at the end of the do block) - but that's a topic I've already discussed over there. And, yes, it would be nice if there were shorthand ways of having an if-else chain, or a try-catch, or whatever, by just saying something like "do if" or "do try", and not needing an entire extra indentation layer, but I currently don't see a great way of solving both that problem and the need pattern matching has for statements-as-expressions, without adding too much stuff to the language.

1 Like

I just had a (probably stupid) idea that I thought of right now. What if iife was a keyword? Consider this syntax:

otherCode();

iife () => {
    requestAnimationFrame(iife);
    graphics.render();
};

moreCode();

Of course, I honour other people's points for how IIFEs should be used. I just had this idea and wanted to share it.

I'm not sure it's possible to introduce a keyword that is also a valid identifier, if it can appear at the start of an expression like that (without confusing JS parsers).

Given that the arguments are always empty (there is one exception (binding this to a local), but that can be handled in other ways), ideally the empty parens wouldn't be required, but then the iife keyword would be parsed as the only param.

Something like this would be nice to have though.

@theScottyJam - Thanks for taking the time to explain. It helped me to understand the other issues you're trying to address better. There's a bunch of stuff there to consider, and rattling off my initial thoughts wouldn't be very helpful to anyone. I just wanted to you to know that I appreciated your time. I'll read your post again, and look through the related threads a bit more closely.

It'd be nice if we could bring these threads together, ideally into a single, coherent proposal. I'm not really competent to take charge of that though.

EDIT: Just looking at the link you posted. I hadn't considered the need for the pattern-matching expressions to be able to function as blocks without creating a new function (that prevents break, return, yield etc applying to the surrounding function). I'm starting to appreciate the conflicting requirements.

It'd be nice if we could bring these threads together, ideally into a single, coherent proposal. I'm not really competent to take charge of that though.

Yeah, discussion happens all over the place for these sorts of things (there's not really a way to avoid that), but probably the best place to find various discussions would be on the do expressions proposal itself, as that's where the general problem it's trying to solve gets explored, along with alternative ideas - having IIFE syntax, for example, I know has been brought up a few times over there.

And, the pattern-matching stuff is just something I personally find to be important. Others might not care as much about that.

Thanks for pointing me in the right direction.

I'm still catching up with that discussion, but the syntax for do-expressions and pattern-matching both look like non-starters to me. As I mentioned before, the following blocks must have the same semantics (in JavaScript):

const result = match (v) {
    ...
}

const result = match(v)
{ 
    ...
}

And the issue with do-expressions is that we already have do-while statements which suffer from placing the predicate after the block, and making the predicate optional will only exacerbate that problem. Any statement that begins with do { really has to be a do-while statement (or do-while expression, if that was supported).

I don't know what to do about either of these issues, and haven't found where it was discussed yet, but am surprised that the proposed syntax wasn't shot down immediately.

I don't recall that there's been much discussion around that, except for the occasional expansion that this is an issue, but they're ok with it, because the vast majority of JS developers put the brace on the same line. When these proposals come in, that coding style will pretty much become a requirement.

I'm not sure there's really another option. If we want new statement syntax while preserving backwards compatibility, we either have to live with this, or as random symbols to the new keywords (which would be pretty ugly). But, it's probably worth discussing - if you can't find any discussions around it on those GitHub repos, feel free to start one.

Thanks, @theScottyJam.

Requiring that braces are on the same line, but only for one construct, fundamentally changes JavaScript syntax, in an odd, implicit, inconsistent way, which may also be an issue for parsers etc. I can't see that ever being standardized (but what do I know).

Using an existing (ideally short) keyword prefix would be one option. Something like:

const result = if match (v) { ... } 

There are other variations that might work better.

There's precedent for making valid variables into keywords in specific contexts, but relying on whitespace to establish that context is the bit that I think crosses a line.

That restriction - referred to as an NLTH (no line terminator here) - is perfectly fine to add, and wouldn't be an issue at all for parsers. The only inconvenience would be if someone was used to using K&R brace style, but since that style can lead to bugs due to ASI, it's best avoided anyways.

That restriction - referred to as an NLTH (no line terminator here) - is perfectly fine to add, and wouldn't be an issue at all for parsers.

I'm not convinced. You may be right technically, but it's not just a matter of whether it can be disambiguated. The fact that the whitespace completely changes the semantics just makes it look and feel hacky. It's subjective, but I doubt I'm alone in not liking it.

What's wrong with using a keyword or reserved word as a prefix? Or combining keywords? Something like:

const result = if as (v) { ... } 

I'm sure we could bikeshed something better, but just to illustrate that alternative approaches exist. We don't need to introduce wacky whitespace rules (on top of JavaScript's already 'unconventional' whitespacing).

We do in order to have the most sensible keyword choices - if we limit ourselves to only using existing reserved words, we'll design a worse language.

Whitespace already completely can change the semantics of a program; it's nothing new in JS.

We do in order to have the most sensible keyword choices - if we limit ourselves to only using existing reserved words, we'll design a worse language.

I agree with this, but there are other means for introducing new keywords. For example, if a keyword (or operator) uses two words (like Python's not in operator), neither word needs to be reserved, as JavaScript never permits an <expression> <expression> (including <name> <name>) grammar. So we could use any two words:

const result = on match (v) { ... }

And we would not become limited to using two new keywords, so we could also address other issues with existing keywords. For example:

switch once (v) { ... } // like `switch`, but doesn't fall through