Pular para o conteúdo

Guia 32 de 53

Handlers e escopos de recursos

Handlers léxicos, substituições tipadas e limpeza determinística de recursos.

Nesta página

Handlers léxicos, substituições tipadas e limpeza determinística de recursos.

Handlers antes do bloco

with handler_value { corpo } avalia e instala o handler antes do corpo. A semântica é a mesma de handle corpo with handler_value. Blocos aninhados restauram o handler externo, e retornos e limpeza mantêm suas fronteiras léxicas. O valor precisa ser um Handler comprovado.

Pratique na By Example · Programa fonte

// 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() })

Escopos de recursos visíveis

using recurso = aquisição { corpo } instala a limpeza após uma aquisição bem-sucedida. O tipo precisa implementar core::cleanup::Cleanup. A limpeza acompanha saídas léxicas e transferências de ownership a desarmam. Um método apenas chamado close não comprova o protocolo.

Pratique na By Example · Programa fonte

// 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))

Substituições tipadas de handlers

Um handler com seletores de operações substitui apenas essas operações e preserva a cobertura de efeitos e o tipo de resposta restantes. Assinaturas e corpos são checados contra o efeito real. A composição preserva o frame e a semântica léxica de abort.

Pratique na By Example · Programa fonte

// 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)

Vários recursos no mesmo escopo

Separe as aquisições com vírgulas no cabeçalho using. Elas executam da esquerda para a direita, e as posteriores podem usar os recursos anteriores. A liberação acontece na ordem inversa. Se uma aquisição falhar, todos os recursos já adquiridos serão liberados.

// 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

Continue aprendendo

Documentação · Pratique na By Example

DOCS / OPINIÃO

Esta página deixou alguma pergunta?

Conte onde a explicação perdeu você. A documentação também faz parte da experiência da linguagem.

Índice global

Encontre seu caminho pela Zolo

Experimente uma ideia

Comece por aqui

9 resultados

9 resultados

enespt-br