Saltar al contenido

26 · Efectos Algebraicos

Fundamentos

Declara un efecto, dispara operaciones con perform e instala un handler con handle … with.

En esta página

Un efecto es una interfaz de operaciones impuras. El bloque effect declara solo firmas — sin cuerpos. Una función que usa el efecto anota with NombreDelEfecto en su firma e invoca cada operación con perform. Quien llama a la función decide el significado real de cada operación dentro de un bloque handle … with { … }.

El ejemplo más simple tiene un único efecto con una única operación. El handler proporciona la única cláusula necesaria, y el valor de la expresión de la cláusula es lo que perform devuelve:

effect IO con una operación; el handler devuelve una cadena por defecto.

01-basic.zolo
Playground
// Feature: algebraic effects — declare, perform, handle
// Syntax: `effect Name { fn op(...) -> T }` declares; `perform Name::op(...)`
// raises; `handle expr with { Name::op(args) => ... }` interprets.
// When to use: dependency-inject impure ops (IO, logging, time,
// randomness) without dragging trait objects through every call.

effect IO {
  fn read(path: str) -> str
}

// `with IO` says: this function performs at least one IO effect.
fn read_config() with IO -> str {
  return perform IO::read("config.toml")
}

// The handler block decides what `IO::read` actually means.
let result = handle read_config() with {
  IO::read(path) => "default config from {path}",
}

print(result)
// expected: default config from config.toml

Un efecto puede agrupar varias operaciones relacionadas — por ejemplo, un Logger con info y warn. El handler debe cubrir todas las operaciones que el cuerpo pueda invocar:

Logger con info/warn; el handler despacha cada una a print.

02-multiple-ops.zolo
Playground
// Feature: effect with multiple operations
// Syntax: an `effect` block can declare several `fn` operations;
// the handler must provide a clause for every operation that
// the effectful code may `perform`.
// When to use: a coherent capability — e.g. a Logger with `info`/`warn`/`error`
// — that you want to swap atomically (test logger, file logger, …).

effect Logger {
  fn info(msg: str)
  fn warn(msg: str)
}

fn run() with Logger {
  perform Logger::info("starting")
  perform Logger::warn("careful")
  perform Logger::info("done")
}

handle run() with {
  Logger::info(msg) => print("[INFO] {msg}"),
  Logger::warn(msg) => print("[WARN] {msg}"),
}
// expected:
//   [INFO] starting
//   [WARN] careful
//   [INFO] done

Las cláusulas del handler reciben los argumentos de la operación como parámetros nombrados. El valor producido por la expresión de la cláusula se devuelve a perform como resultado — lo que permite mocks determinísticos y stubs computados:

Efecto Math con add y double; el handler calcula el resultado real a partir de los argumentos.

03-handler-args.zolo
Playground
// Feature: handler clauses can use the operation arguments
// Syntax: each clause `Op(arg1, arg2) => <expr>` binds the args
// and the expression's value is the result of `perform`.
// When to use: any effect where the result depends on the input —
// pure mocks, deterministic stubs, computed responses.

effect Math {
  fn add(a: int, b: int) -> int
  fn double(n: int) -> int
}

fn compute() with Math -> int {
  let x = perform Math::add(2, 3)
  let y = perform Math::double(x)
  return y
}

let result = handle compute() with {
  Math::add(a, b) => a + b,
  Math::double(n) => n * 2,
}
print(result)
// expected: 10

Desafío

En el ejemplo de Logger, agrega una tercera operación error(msg: str) al efecto y una cláusula correspondiente en el handler. ¿Qué pasa si omites la cláusula?

Consulta también

Índice global

Encuentra tu camino por Zolo

Prueba una idea

Empieza aquí

9 resultados

9 resultados

enespt-br