Skip to content

Working with loops #676

@FunctionDJ

Description

@FunctionDJ

I struggly a lot converting a code base to using neverthrow in places where functions are called inside of loops.

Example:

function damageUnitsOnCityTiles(player) {
  for (const unit of player.team.getEnemyUnits()) {
    if (unit.getTile().type === "city") { // <-- previously, getTile() used throw, but now it returns Result<Tile, E>, so this code expectedly errors
      unit.damageUnit(3)
    }
  }
}

How do I rewrite such code to hand over any error upwards? In this example I'd prefer aborting early if an error is encountered and returning it (no need to try all calls and collect all errors).


One possible solution (I think) using safeTry():

function damageUnitsOnCityTiles(player) {
  return safeTry(function* () {
    for (const unit of player.team.getEnemyUnits()) {
      const tile = yield* unit.getTile() // eslint-plugin-neverthrow error: Result must be handled with either of match, unwrapOr or _unsafeUnwrap.
      if (tile.type === "city") {
        unit.damageUnit(3)
      }
    }

    return ok()
  })
}

Cons: Lint error + i don't really like generators 😶


Another solution using Result.combine():

function damageUnitsOnCityTiles(player) {
  const unitsAndTilesResult = Result.combine(
    player.team.getEnemyUnits().map((unit) => unit.getTile().map((tile) => ({ unit, tile }))),
  );

  return unitsAndTilesResult.map((unitsAndTiles) => {
    for (const { unit, tile } of unitsAndTiles) {
      if (tile.type === "city") {
        unit.damageUnit(3)
      }
    }
  });
}

Pros: No lint error
Cons: No abort-early + very boilerplate-y with the unitsAndTilesResult bag variable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions