Blocos Try Transacionais em ValueScript

Apr 02 2023
Exceções são difíceis de acertar. Um problema particularmente complicado que é fácil de ignorar é a possibilidade de avaliação parcial dentro dos blocos try.

Exceções são difíceis de acertar. Um problema particularmente complicado que é fácil de ignorar é a possibilidade de avaliação parcial dentro trydos blocos.

Considere o seguinte programa JavaScript:

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 });

Outra opção é salvar uma cópia das variáveis ​​que podem ser alteradas e restaurá-las em 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. Você pode não fazer isso porque é prolixo e parece desnecessariamente defensivo
  2. Requer uma copyoperação a ser definida
  3. Se a copyoperação existir, pode não ser eficiente
  4. Se a.poptiver efeitos colaterais, esses efeitos colaterais não serão revertidos

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. Extremamente prolixo
  2. a.unPop(item)exige que entendamos que esta operação é necessária quando b.push(item)falha, o que ainda é fácil de perder

No ValueScript, tryos blocos são transacionais — eles são executados até a conclusão ou são revertidos.

Nosso programa original faz o que queríamos sem nenhuma modificação:

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. “Você pode não fazer isso”
    — O compilador ValueScript insere as instruções automaticamente
  2. “Requer copyoperação”
    — ValueScript usa semântica de valor, apenas usa aCopy = a, e isso é equivalente a copiar
  3. “ copypode não ser eficiente”
    — ValueScript usa copy-on-write até o final, é bastante eficiente
  4. “Efeitos colaterais a.popnão serão revertidos”
    — ValueScript não tem efeitos colaterais

* algumas funções estrangeiras impressionantes podem incluir operações de reversão e estas seriam chamadas automaticamente

ValueScript é um dialeto do TypeScript com semântica de valor. Ele tem um playground online que inclui uma demonstração do programa deste artigo e é de código aberto .