Saltar al contenido

resume y abort

En esta página

Por defecto, la expresión de una cláusula del handler es el valor devuelto a perform. A veces el handler necesita hacer trabajo antes de decidir qué valor retornar — validar, registrar, contar. resume(v) acorta ese camino: el handler ejecuta lo que necesite y solo entonces reanuda la computación con v como resultado del perform:

El handler de Ask::ask registra la pregunta en una lista y luego hace resume con la respuesta correcta.

07-explicit-resume.zolo
Playground
// Feature: explicit `resume(v)` — short-circuit the arm with `v` as the

// value returned to `perform`. Lets a handler do work before resuming

// (log, validate, count) without coupling the body's last expression to

// the resume value.

// Syntax: `resume(v)` is reserved inside any `handle ... with { ... }`

// arm body. Outside an arm body it falls through to be treated as an

// ordinary call (so user code that defines a `resume` fn still works).

// When to use: you want the handler to do something *before* handing

// control back, or branch on the operation args before deciding what

// value to send back.


effect Ask {
  fn ask(question: str) -> str
}

fn survey() with Ask -> str {
  let name = perform Ask::ask("What is your name?")
  let color = perform Ask::ask("What is your favourite colour?")
  return "{name} likes {color}"
}

let asked = []

let answer = handle survey() with {
  Ask::ask(q) => {
    asked.push(q)

    // Do work first, then resume with a chosen default.

    if q.contains("name") {
      resume("Alice")
    }

    resume("blue")
  },
}

print(answer)
for q in asked { print("- asked: {q}") }
// expected:

//   Alice likes blue

//   - asked: What is your name?

//   - asked: What is your favourite colour?

A veces no hay ningún valor razonable que devolver a perform — la operación falló y la continuación sería inútil. abort(v) descarta toda la continuación pendiente y hace que v se convierta en el resultado de toda la expresión handle. Es equivalente a un throw/return de larga distancia, sin necesidad de transportar Result<T, E> por toda la cadena de llamadas:

Fail::fail llama a abort(-1); el camino feliz resulta en la suma normal, el camino con error devuelve -1.

08-terminal-handlers.zolo
Playground
// Feature: terminal handlers via `abort(v)` — discards the

// continuation; `v` becomes the value of the enclosing `handle`

// expression. Equivalent to `throw`/`return` of long distance, without

// exceptions or `Result<T, E>` wiring.

// Syntax: `abort(v)` is reserved inside any `handle ... with { ... }`

// arm body. It's tagged with the *specific* handle frame, so a terminal

// arm aborts only to its own `handle` — never to an outer one.

// When to use: failure paths, early exits, parse errors — anywhere the

// continuation would be useless because the operation can't sensibly

// return a value of the declared type.


effect Fail {
  fn fail(reason: str) -> int
}

fn parse_int(s: str) with Fail -> int {
  // Tiny mock-parser: accept "1".."5" — anything else fails.

  if s == "1" { return 1 }
  if s == "2" { return 2 }
  if s == "3" { return 3 }
  if s == "4" { return 4 }
  if s == "5" { return 5 }
  perform Fail::fail("not in 1..5: {s}")
  return 0  // unreachable: handler aborts

}

fn sum_of(inputs: [str]) with Fail -> int {
  var total = 0
  for s in inputs {
    total = total + parse_int(s)
  }
  return total
}

// Happy path: every input parses → normal resume of -1 never happens.

let ok = handle sum_of(["1", "2", "3"]) with {
  Fail::fail(_r) => abort(-1),
}
print("ok: {ok}")
// expected: ok: 6


// Bad input: handler aborts the whole sum with -1 as the result.

let bad = handle sum_of(["1", "oops", "3"]) with {
  Fail::fail(reason) => {
    print("- aborted: {reason}")
    abort(-1)
  },
}
print("bad: {bad}")
// expected:

//   - aborted: not in 1..5: oops

//   bad: -1

La diferencia es conceptual: resume continúa la computación con un valor; abort la termina. Ambas se usan dentro de una cláusula de handler y afectan solo el frame handle que las instaló — un abort nunca escapa a un handler externo.

Desafío

En 08-terminal-handlers.zolo, agrega una operación warn(msg: str) al efecto y usa resume(0) en la cláusula correspondiente. ¿Qué cambia en el flujo?

Buscar en Zolo

9 resultados

enespt-br