resume e abort
Nesta página
Por padrão, a expressão de uma cláusula do handler é o valor devolvido ao perform.
Às vezes o handler precisa fazer trabalho antes de decidir qual valor retornar —
validar, registrar, contar. resume(v) encurta esse caminho: o handler executa o que
precisar e só então retoma a computação com v como resultado do perform:
Handler de Ask::ask registra a pergunta numa lista e depois resume com a resposta correta.
// 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?
Às vezes não há nenhum valor razoável para devolver ao perform — a operação falhou
e a continuação seria inútil. abort(v) descarta toda a continuação pendente e faz
v se tornar o resultado da expressão handle inteira. É equivalente a um
throw/return de longa distância, sem precisar carregar Result<T, E> por toda a
cadeia de chamadas:
Fail::fail faz abort(-1); o caminho feliz resulta na soma normal, o caminho com erro retorna -1.
// 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
A diferença é conceitual: resume continua a computação com um valor; abort a
encerra. Ambas são usadas dentro de uma cláusula de handler e afetam apenas o
frame handle que as instalou — um abort nunca escapa para um handler externo.
Desafio
Em 08-terminal-handlers.zolo, adicione uma operação warn(msg: str) ao efeito e
use resume(0) na cláusula correspondente. O que muda no fluxo?
Veja também