Saltar al contenido

Guía 32 de 53

Handlers y ámbitos de recursos

Handlers léxicos, reemplazos tipados y limpieza determinista de recursos.

En esta página

Handlers léxicos, reemplazos tipados y limpieza determinista de recursos.

Handlers antes del bloque

with handler_value { cuerpo } evalúa e instala el handler antes del cuerpo. Tiene la misma semántica que handle cuerpo with handler_value. Los bloques anidados restauran el handler exterior; retornos y limpieza conservan sus límites léxicos. El valor debe ser un Handler comprobado.

Practica en By Example · Programa fuente

// Prefix handler installation keeps the interpretation next to its scope.
// expected:
// memory
// inner
// memory

effect Config {
    fn read() -> str
}

fn load() with Config -> str = perform Config::read()

let memory = handler {
    Config::read() => "memory",
}

let inner = handler {
    Config::read() => "inner",
}

print(with memory { load() })

let nested = with memory {
    let answer = with inner { load() }
    answer
}
print(nested)
print(with memory { load() })

Alcances visibles de recursos

using recurso = adquisición { cuerpo } instala la limpieza tras una adquisición exitosa. El tipo debe implementar core::cleanup::Cleanup. La limpieza sigue las salidas léxicas y las transferencias de propiedad la desactivan. Un método llamado close no demuestra el protocolo.

Practica en By Example · Programa fuente

// Resource scopes install cleanup after successful acquisition.
var acquired = 0
struct Resource { id: int }
fn acquire(id: int) -> Result<Resource, str> {
    acquired += 1
    if id < 0 { .Err("acquisition") } else { .Ok(Resource { id }) }
}
impl core::cleanup::Cleanup for Resource {
    fn close(self) { print("close"); print(self.id) }
}
fn read(id: int) -> Result<int, str> {
    let value = using file = acquire(id)? {
        defer print("body-defer")
        file.id + 10
    }
    Result::Ok(value)
}
print(read(1).unwrap())
print(read(-1).is_err())
print(acquired)
fn early() -> int {
    using file = Resource { id: 2 } { return 22 }
    0
}
print(early())
fn fail() -> Result<int, str> { .Err("body") }
fn propagated() -> Result<int, str> {
    let value = using file = Resource { id: 3 } { fail()? }
    Result::Ok(value)
}
print(propagated().is_err())
var count = 0
:outer loop {
    count += 1
    using file = Resource { id: count + 3 } {
        defer print("loop-defer")
        if count == 1 { continue :outer }
        break :outer
    }
}
let tail = using outer = Resource { id: 6 } {
    let inner = using inner = Resource { id: 7 } { inner.id }
    inner + outer.id
}
print(tail)
@linear struct Owned { id: int }
impl core::cleanup::Cleanup for Owned {
    fn close(self) { print("owned-close"); print(self.id) }
}
impl Owned {
    fn finish(sink self) { self.close(); let Owned { .. } = self }
}
using owned = Owned { id: 8 } { 0 }
using owned = Owned { id: 9 } {
    var __zolo_live_0 = true
    owned.finish()
    assert(__zolo_live_0)
    0
}
fn consume(sink owned: Owned) { owned.close(); let Owned { .. } = owned }
fn seven() -> int { 7 }
fn consume_with(sink owned: Owned, number: int) { assert(number == 7); consume(owned) }
using owned = Owned { id: 10 } {
    let __zolo_arg_0 = 13
    consume_with(owned, seven())
    assert(__zolo_arg_0 == 13)
    0
}

let transferred = using owned = Owned { id: 11 } { owned }
consume(transferred)

// The implicit cleanup owns the original binding even when the body shadows it.
let shadow = using file = Resource { id: 12 } {
    let file = Resource { id: 13 }
    file.id
}
print(shadow)
// Generic sink transfer hands the resource to the caller and disarms cleanup.
fn moved<T: core::cleanup::Cleanup>(sink value: T) -> T {
    using resource = value { resource }
}
consume(moved(Owned { id: 14 }))
macro scoped(value) { using resource = $value { consume(resource); 7 } }
print(scoped!(Owned { id: 15 }))
print(scoped!(Owned { id: 16 }))

// An owned generic factory temporary keeps its cleanup obligation.
fn acquire_owned() -> Owned { Owned { id: 17 } }
fn scoped_factory<T: core::cleanup::Cleanup>(factory: fn() -> T) -> int {
    let acquired = factory()
    using resource = acquired { 7 }
}
print(scoped_factory(acquire_owned))

Reemplazos tipados de handlers

Un handler con selectores de operaciones sustituye solo esas operaciones y conserva la cobertura de efectos y el tipo de respuesta restantes. Las firmas y cuerpos se comprueban contra el efecto real. La composición conserva el frame y la semántica léxica de abort.

Practica en By Example · Programa fuente

// Composition preserves base evaluation, closures and lexical cleanup.
effect Clock { fn now() -> int; fn zone() -> str }
effect Store { fn put(value: int) -> int }
var calls = 0
var builds = 0
var events = ""
let fixed_time = 42
let __zolo_frame_id_0 = 0
let __zolo_handler_override_compose_0 = 0
let real_world: handler<Clock + Store, int> = handler {
  Clock::now() => resume(1),
  Clock::zone() => resume("UTC"),
  Store::put(value) => resume(value + 1),
}
fn choose(value: handler<Clock + Store, int>) -> handler<Clock + Store, int> { value }
// Forwarding an opaque handler parameter does not establish composition origin.
let test_world = ({ builds += 1; real_world }) with {
  Clock::now() => { calls += 1; defer { events += "defer/" }; fixed_time + __zolo_frame_id_0 + __zolo_handler_override_compose_0 },
}
print(with test_world { perform Clock::now() + perform Store::put(2) })
print(with test_world { perform Clock::zone(); 7 })
print(with test_world { perform Clock::now() })
print(calls)
print(events)
print(builds)
let twice = test_world with { Store::put(value: int) => value + 10 }
print(with twice { perform Store::put(2) })
fn lexical(value: handler<Clock + Store, int>) -> int {
  defer { events += "caller/" }
  with value { return 9 }
  0
}
print(lexical(twice))
print(events)

Varios recursos en el mismo ámbito

Separe las adquisiciones con comas en el encabezado using. Se ejecutan de izquierda a derecha y las posteriores pueden usar los recursos anteriores. La liberación ocurre en orden inverso. Si una adquisición falla, se liberan todos los recursos ya adquiridos.

// Each successful acquisition installs cleanup before the next one starts.
struct File { name: str }
fn open(name: str) -> File { File { name } }
impl core::cleanup::Cleanup for File {
    fn close(self) { print("close {self.name}") }
}
let copied = using source = open("source"), target = open("target") {
    print("{source.name} -> {target.name}")
    3
}
// expect: source -> target
// expect: close target
// expect: close source
print(copied) // expect: 3

Sigue aprendiendo

Documentación · Practica en By Example

DOCS / OPINIÓN

¿Esta página dejó alguna pregunta?

Cuéntanos dónde te perdió la explicación. La documentación también forma parte de la experiencia del lenguaje.

Índice global

Encuentra tu camino por Zolo

Prueba una idea

Empieza aquí

9 resultados

9 resultados

enespt-br