El libro

Apéndice D · Catálogo de efectos del stdlib

Este apéndice resume los efectos que el stdlib expone. Es material de referencia: ante una firma : Unit / X en la documentación de una función, vienes acá a confirmar qué provee X.

La especificación completa vive en github.com/kaikailang-org/kaikai/docs/effects-stdlib.md. Acá mostramos la declaración del efecto y para qué sirve.

D.1 IO básico

Console

effect Console {
  print(s: String)  : Unit
  eprint(s: String) : Unit
}

Imprimir a stdout y stderr. Cada operación agrega un newline. El handler por defecto del runtime escribe al descriptor de archivo correspondiente.

Stdin

effect Stdin {
  read_line() : Result[String, String]
}

Leer una línea de entrada estándar. Devuelve Err(motivo) en caso de EOF o error de lectura.

Env

effect Env {
  get(name: String) : Option[String]
  args()            : [String]
}

Acceso a variables de entorno (PATH, HOME, etc.) y a los argumentos de línea de comando (argv).

File

effect File {
  read_file(path: String)                  : Result[String, String]
  write_file(path: String, content: String): Result[Unit, String]
  append(path: String, content: String)    : Result[Unit, String]
  exists(path: String)                     : Bool
  delete(path: String)                     : Result[String, Unit]
  rename(from: String, to: String)         : Result[String, Unit]
}

Operaciones sobre archivos. Para todo lo que sea no-trivial (streams, directorios, permisos), ver fs.dir y los módulos auxiliares.

D.2 Tiempo y aleatoriedad

Clock

effect Clock {
  now()              : Int          # nanosegundos desde epoch
  sleep(ms: Int)     : Unit / Cancel
}

Reloj y sleep. sleep es punto de yield (carga Cancel).

Random

effect Random {
  int(min: Int, max: Int)   : Int
  real()                    : Real
  shuffle[a](xs: [a])       : [a]
}

Generación pseudo-aleatoria, no apta para criptografía. Para secretos, ver SecureRandom.

SecureRandom

effect SecureRandom {
  bytes(n: Int) : [Byte]
}

Bytes aleatorios criptográficamente seguros (vía /dev/urandom o equivalente del sistema).

D.3 Red

NetTcp

effect NetTcp {
  connect(host: String, port: Int)   : Result[Conn, String]
  listen(host: String, port: Int)    : Result[Listener, String]
  accept(l: Listener)                : Result[Conn, String]
  send(c: Conn, data: [Byte])        : Result[Int, String]
  recv(c: Conn, max: Int)            : Result[[Byte], String]
  close(c: Conn)                     : Unit
}

Sockets TCP byte-level. Las operaciones bloqueantes (connect, accept, send, recv) suspenden la fibra vía el reactor del runtime cuando aterrice (v1 las hace bloqueantes al OS thread).

NetUdp y NetDns

UDP (bind/send/recv/close) y DNS (resolve). Mismo estilo que NetTcp. El alias Net = NetTcp + NetUdp + NetDns es útil cuando una función usa los tres.

D.4 Procesos y señales

Process

effect Process {
  run(cmd: String, args: [String]) : Result[ProcessResult, String]
  pid()                            : Int
}

Ejecutar comandos externos como subprocesos. ProcessResult contiene exit_code, stdout y stderr.

Signal

type Sig = SigInt | SigTerm | SigHup | SigUsr1 | SigUsr2

effect Signal {
  on(sig: Sig)  : Unit
  off(sig: Sig) : Unit
  await()       : Sig
}

Esperar a una señal POSIX sin bloquear las demás fibras. on(sig) subscribe el proceso a sig; el runtime bloquea la señal a nivel del proceso y el kernel encola la entrega en vez de aplicar la disposición por defecto. await() parkea la fibra que llama hasta que llegue cualquiera de las señales subscritas y devuelve la variante Sig correspondiente. off(sig) desuscribe.

El handler default vive alrededor de main cuando Signal está en la fila. Solo una fibra puede estar en await() a la vez; un segundo llamado concurrente entra en pánico. Útil para limpieza ordenada al apagar el proceso — Signal.on(SigInt); match Signal.await() { ... } reemplaza el típico handler de SIGINT escrito a mano.

D.5 Estado

State[T]

effect State[T] {
  get() : T
  set(v: T) : Unit
}

El efecto canónico para llevar estado mutable de forma encapsulada. La sintaxis var nombre = init es azúcar sobre un handle de State[T] (cap. 12 §12.7).

Reader[T] y Writer[W]

effect Reader[T] {
  ask() : T
}

effect Writer[W] {
  tell(w: W) : Unit
}

Entorno de lectura (configuración inmutable) y acumulación de salida (logging, traza). Patrones clásicos del cálculo de efectos.

Mutable

effect Mutable {
  ref_make[T](init: T)      : Ref[T]
  ref_get[T](r: Ref[T])     : T
  ref_set[T](r: Ref[T], v: T): Unit
  array_make[T](n: Int, init: T) : Array[T]
  array_length[T](a: Array[T])   : Int
  array_get[T](a: Array[T], i: Int)        : T
  array_set[T](a: Array[T], i: Int, v: T)  : Unit
  array_grow[T](a: Array[T], n: Int, init: T): Unit
}

El efecto detrás de Ref[T] y Array[T]. Sigue la disciplina de efectos observables (cap. 12 §12.7): un array_set requiere Mutable en la fila solo cuando la mutación es visible para quien llama. Un array creado local y devuelto no requiere Mutable.

D.6 Errores y control

Fail

effect Fail {
  fail(msg: String) : Nothing
}

Abortar con un mensaje. La operación devuelve Nothing (el tipo vacío), así que el sistema de tipos garantiza que no se puede llamar a resume después de Fail.fail. Es el patrón canónico para “excepción ligera” en kaikai.

Cancel

effect Cancel {
  raise() : Nothing
}

Cancelación cooperativa. El scheduler inyecta Cancel.raise() en una fibra cancelada en el próximo punto de yield. La fibra puede instalar un handler de Cancel para limpieza (cap. 13).

D.7 Concurrencia

Spawn

effect Spawn {
  spawn[T, e](f: () -> T / e) : Fiber[T]
  await[T](f: Fiber[T])       : T
  select[T](fs: [Fiber[T]])   : T
  yield()                     : Unit
  cancel[T](f: Fiber[T])      : Unit
}

Crear fibras, esperarlas, racear, ceder, cancelar. nursery { n -> ... } del cap. 13 es azúcar sobre handle ... with Spawn as n { ... }.

Actor[Msg]

effect Actor[Msg] {
  self()                         : Pid[Msg]
  send(pid: Pid[Msg], msg: Msg)  : Unit / Cancel
  receive()                      : Msg / Cancel
}

El efecto que da forma al modelo de actores del cap. 14. with_mailbox { ... } y spawn_actor(...) son los wrappers del stdlib que instalan este handler.

effect Link {
  link(pid: Pid[_]) : Unit
}

effect Monitor {
  monitor(pid: Pid[_])         : MonitorRef
  demonitor(ref: MonitorRef)   : Unit
}

Supervisión al estilo BEAM (cap. 14 §14.6). Links son bidireccionales: si uno cae, el otro recibe Cancel.raise(). Monitores son unidireccionales: el observador recibe un mensaje MonitorDown cuando el observado termina.

D.8 Interoperabilidad

Ffi

effect Ffi

El efecto que cargan todas las funciones declaradas con extern "C" fn. Sin operaciones propias: es un marcador para que el sistema de tipos sepa qué funciones tocan código no auditado por kaikai. El capítulo 16 §16.12 cubre la sintaxis de declaración, el mapeo de tipos en el borde, el enlazado con shims C, y qué soporta y qué no FFI v1.

D.9 Composición: el alias Io

type Io = Console + Stdin + Env + File

Bundle de los efectos más comunes para IO al sistema operativo. Una función que dice / Io está declarando que puede tocar consola, leer stdin, leer variables de entorno y manipular archivos. Es el equivalente a “esta función no es pura, hace cosas con el sistema”.

D.10 Handlers por defecto

Cuando main declara uno de estos efectos en su fila, el runtime instala automáticamente un handler por defecto:

  • Console, Stdin, Env, File → IO al sistema.
  • Clock, Random, SecureRandom → reloj y RNG del sistema.
  • NetTcp, NetUdp, NetDns → POSIX sockets.
  • Process, Signal → llamadas POSIX.
  • Mutable → asignaciones reales en heap.
  • Spawn, Cancel → el scheduler de fibras del runtime.

Estos handlers se pueden interceptar: cualquier handle ... with X { ... } que el usuario instale gana sobre el handler del runtime para todo el bloque del body. Eso es lo que permite mocking en tests, capturar la salida, simular el reloj, etc.

← Volver al índice