Skip to content

Jabher/proposal-do-while-lexical-scope

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 

Repository files navigation

Do..while extended lexical scope

Include the condition inside of while(...) expression into the lexical scope of do {...} block in a manner that threats following code as a valid:

do {
  const shouldQuit = getShouldIQuit();
} while (!shouldQuit)

Status

Author(s): Vsevolod Rodionov

Stage: -1

Motivation

Why is this important to have in the JavaScript language?

Unlike for(), for...of and for...in blocks, who are iterating over known entity, while and do...while blocks are commonly used when break condition is unclear. Often the break condition is actually defined inside the block; however, there is no common way to declare the exit condition that is defined inside the block clearly. 2 most common approaches are:

let shouldQuit = false;
do {
  shouldQuit = getShouldIQuit()
} while (!shouldQuit)

and

while (true) {
  const shouldQuit = getShouldIQuit();
  if (shouldQuit) {
    break;
  }
}

Both of them have issues:

Option 1 is mutating the external state, is verbose and is can pollute the higher-level lexical scope with variable only needed for inner scope. This is still declarative approach: code states, that it has a computation block, and it has an break post-condition.

Option 2 has 3 issues:

  • despite the fact that it has post-condition, it is declared in pre-condition manner (while () {} instead of do {} while ())
  • it is using break. Usage of this statement is controversial and discussed a lot, e.g.: 1, 2, 3, 4
  • its approach us redundantly imperative: it increases cognitive complexity; instead of clearly stated break condition the one who sees this block for a first time has to look though it to discover the break control flow statements

Use cases

angular

// before
while (true) {
  const tagCloseStart = this._cursor.clone();
  const foundEndMarker = endMarkerPredicate();
  this._cursor = tagCloseStart;
  if (foundEndMarker) {
    break;
  }
  parts.push(this._readChar(decodeEntities));
}
// after
do {
  const tagCloseStart = this._cursor.clone();
  const foundEndMarker = endMarkerPredicate();
  this._cursor = tagCloseStart;
  if (!foundEndMarker) {
    parts.push(this._readChar(decodeEntities));
  }
} while (!foundEndMarker)

typescript

// before
while (true) {
    command = `${npmPath} install --ignore-scripts ${(toSlice === packageNames.length ? packageNames : packageNames.slice(sliceStart, sliceStart + toSlice)).join(" ")} --save-dev --user-agent="typesInstaller/${tsVersion}"`;
    if (command.length < 8000) {
        break;
    }

    toSlice = toSlice - Math.floor(toSlice / 2);
}
// after
do {
    command = `${npmPath} install --ignore-scripts ${(toSlice === packageNames.length ? packageNames : packageNames.slice(sliceStart, sliceStart + toSlice)).join(" ")} --save-dev --user-agent="typesInstaller/${tsVersion}"`;
    toSlice = toSlice - Math.floor(toSlice / 2);
} while (command.length >= 8000)

yargs-parser

// before
var change = true
while (change) {
  change = false
  for (var i = 0; i < aliasArrays.length; i++) {
    for (var ii = i + 1; ii < aliasArrays.length; ii++) {
      if (intersect.length) {
        change = true
      }
    }
  }
}
// after
do {
  let change = false;
  for (var i = 0; i < aliasArrays.length; i++) {
    for (var ii = i + 1; ii < aliasArrays.length; ii++) {
      if (intersect.length) {
        change = true
      }
    }
  }
} while (change)

Implementations

Polyfill/transpiler implementations

Babel plugin for this proposal can be implemented in a short period of time.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published