Transaktionale Try-Blöcke in ValueScript

Apr 02 2023
Ausnahmen sind schwer richtig zu machen. Ein besonders kniffliges Problem, das leicht zu übersehen ist, ist die Möglichkeit der teilweisen Auswertung innerhalb von Try-Blöcken.

Ausnahmen sind schwer richtig zu machen. Ein besonders kniffliges und leicht zu übersehendes Problem ist die Möglichkeit der Teilauswertung innerhalb von tryBlöcken.

Betrachten Sie das folgende JavaScript-Programm:

let a = new SmallQueue(["item1"]);
let b = new SmallQueue(["item2", "item3", "item4"]);

try {
  const item = a.pop();
  b.push(item);
} catch (e) {
  console.error("Failed to move item from a to b", e);
}

console.log({ a, b });

{
  a: [],
  b: ["item2", "item3", "item4", "item1"],
}

{
  a: [],
  b: ["item2", "item3", "item4"],
}

let a = new SmallQueue(["item1"]);
let b = new SmallQueue(["item2", "item3", "item4"]);

try {
  if (b.isFull()) {
    throw new Error("b is full");
  }

  const item = a.pop();
  b.push(item);
} catch (e) {
  console.error("Failed to move item from a to b", e);
}

console.log({ a, b });

Eine andere Möglichkeit besteht darin, eine Kopie der Variablen zu speichern, die sich möglicherweise ändern, und sie wiederherzustellen unter catch:

let a = new SmallQueue(["item1"]);
let b = new SmallQueue(["item2", "item3", "item4"]);

const aCopy = a.copy();
const bCopy = b.copy();

try {
  const item = a.pop();
  b.push(item);
} catch (e) {
  a = aCopy;
  b = bCopy;
  console.error("Failed to move item from a to b", e);
}

console.log({ a, b });

  1. Sie tun es vielleicht nicht, weil es wortreich ist und sich unnötig defensiv anfühlt
  2. Es muss eine copyOperation definiert werden
  3. Wenn die copyOperation vorhanden ist, ist sie möglicherweise nicht effizient
  4. Wenn a.popNebenwirkungen auftreten, werden diese Nebenwirkungen nicht rückgängig gemacht

let a = new SmallQueue(["item1"]);
let b = new SmallQueue(["item2", "item3", "item4"]);

let item;

try {
  item = a.pop();
} catch (e) {
  console.error("Failed to move item from a to b", e);
  console.log({ a, b });
  return;
}

try {
  b.push(item);
} catch (e) {
  a.unPop(item);
  console.error("Failed to move item from a to b", e);
}

console.log({ a, b });

  1. Extrem wortreich
  2. a.unPop(item)erfordert, dass wir verstehen, dass diese Operation erforderlich ist, wenn b.push(item)sie fehlschlägt, was immer noch leicht zu übersehen ist

In ValueScript trysind Blöcke transaktional – sie werden entweder bis zum Ende ausgeführt oder zurückgesetzt.

Unser ursprüngliches Programm macht das, was wir wollten, ohne Änderungen:

let a = new SmallQueue(["item1"]);
let b = new SmallQueue(["item2", "item3", "item4"]);

try {
  const item = a.pop();
  b.push(item);
} catch (e) {
  console.error("Failed to move item from a to b", e);
}

console.log({ a, b });
{
  a: ["item1"],
  b: ["item2", "item3", "item4"],
}

  1. „Sie könnten es nicht tun“
    – Der ValueScript-Compiler fügt die Anweisungen automatisch ein
  2. copyVorgang erforderlich“
    – ValueScript verwendet Wertsemantik, es verwendet nur aCopy = a, und dies entspricht dem Kopieren
  3. copyMöglicherweise nicht effizient“
    – ValueScript verwendet Copy-on-Write ganz nach unten, es ist ziemlich effizient
  4. „Nebenwirkungen von a.popwerden nicht rückgängig gemacht“
    – ValueScript hat keine Nebeneffekte

* Einige großartige Fremdfunktionen könnten Wiederherstellungsoperationen enthalten, und diese würden automatisch aufgerufen

ValueScript ist ein Dialekt von TypeScript mit Wertsemantik. Es hat einen Online-Spielplatz, der eine Demonstration des Programms dieses Artikels enthält , und es ist Open Source .