samuelgoto / proposal-block-params

A syntactical simplification in JS to enable DSLs
204 stars 8 forks source link

continue and break #8

Open samuelgoto opened 6 years ago

samuelgoto commented 6 years ago

This is a collection of alternatives on how to handle break and continue.

Read this first

There is a lot of context/background and ideas considered in these threads. Please read them first.

Introduction

continue and break are interesting cases because there are two valid uses:

for (let i = 0; i < 10; i++) {
  unless (i == 5) {
    // You'd expect the continue to apply to the
    // lexical for, not to the unless
    continue;
  }
}

Whereas:

for (let i = 0; i < 10; i++) {
  foreach (array) {
    if (::item == 5) {
      // You'd expect the continue here to apply to
      // the foreach, not the lexical for.
      continue;
    }
  }
}

Here are some options to be explored / compared.

Alternatives

Disallow

Like kotlin, In this formulation, we would disallow break and continue inside block params. This would lead into block params not supporting iterations or early returns. I believe this wouldn't corner ourselves, so could be a smart way to sequence things.

Inline

Inspired by kotlin's inline functions, in this formulation, the semantics of break and continue would be determined evaluating the result of inlining (metaphorically, not literally wrt performance) the entire function call (e.g. like a C macro works). For example:

// ... for example ...
function unless(expr, block) {
  if (!expr) {
    block();
  }
}
for (let i = 0; ) {
  unless (i % 2 == 0) {
    continue;
  }
}
// ... gets interpreted as ...
for (let i = 0; ) {
  if (!(i % 2 == 0)) {
    continue;
  }
}
// ... i.e. the continue applies to the for ...

Whereas

// ... whereas foreach ...
function foreach(collection, block) {
  for (let v of collection) {
    block.call({item: v});
  }
}
foreach ([1, 2, 3]) {
  if (::item % 2 == 0) {
    continue;
  }
}
// ... gets inline as ...
let collection = [1, 2, 3];
for (let v of collection) {
  this.item = v;
  if (::item % 2 == 0) {
    // gets applied to the internal for-loop
    // created inside for-each
    continue;
  }
}

Modifier

Inspired by kotlin's inline functions, in this formulation, we could make the semantics switch based on the declaraction of the function.

inline function unless(expr, block) {
  // ... breaks and continues bound lexically ...
}
function foreach (collection) {
  // ... breaks and continues bound locally ...
}

Call modifier

inspired by java's for, in this formulation the distinction in behavior would be done at all site. For example:

loop foreach () {
  // ... breaks and continues bound lexically ...
}
foreach () {
  // ... breaks and continue bound locally ...
}

Standard Exceptions

One interesting approach here is to make continue and break throw a special standard Exception (say, ContinueException and BreakException), which can then be re-thrown or not (and understood by the lexical blocks).

foreach (array) {
  continue;
}
// ... is sugar for ...
foreach (array, function() {
  throw new ContinueException();
});
// ... which can be caught and re-thrown depending on context ...

Comes with challenges.

Labels

Another interesting approach here is to make continue and break behave lexically, but explicit labels to mean the local ones. For examples:

unless (...) {
  continue; // lexical scope
}
foreach (...) {
  continue foreach; // local scope
}

This pulls the responsibility to the user to know the distinction, which I'm not sure if the right trade-off.

NOTE(goto): kotlin is planning to include continue/break but for inline functions only, meaning that you can't do a foreach with continue/break.

NOTE(goto): would love to hear about alternatives here.

dead-claudia commented 6 years ago

Oh, and here's a couple fun ones that complicate everything: yield and await. They suspend the execution context, making standard functions impossible to use without implementing stackful coroutines under the hood for DSLs (which I expect to be a no-go, since generators are stackless coroutines).