Generators (yields)
Posicionamento
Seção intitulada “Posicionamento”Generators são para sequências lazy e iteração dentro de um único processo. Não são para concorrência, que é papel de processos e channels.
Coroutine-style (bidirecional) é redundante: um “acumulador” que recebe e retorna valores é só um processo com um channel Channel[<-In, ->Out].
Generator[T] é first-class
Seção intitulada “Generator[T] é first-class”@generator fn fibonacci() -> int { var a, b := 0, 1 loop { yield a a, b = b, a + b }}
loop n in fibonacci().take(10) { ... }Restrição (stackless). Sendo zero-alocação e no mesmo processo (acima), o generator é uma state-machine stackless, então yield só acontece no nível do próprio generator, não dentro de uma função comum que ele chame (uma função comum não tem como suspender o generator que a chamou). Para suspensão arbitrária através de chamadas, o que você quer é um processo (stackful, seção 3), não um generator. Com o scheduler ele compõe bem: um next() que toca io.* suspende o processo (stackful), e a state-machine do generator é preservada nessa suspensão.
Abandonar um generator roda os defers dele. O consumidor pode parar antes — break no laço, return de dentro dele, ou esgotar um take(n) antes da fonte secar — e o generator é então desmontado no ponto onde estava suspenso, rodando tudo que devia ali, do escopo mais interno pra fora e cada escopo em LIFO. Um generator que adquire um recurso portanto o libera mesmo que ninguém o drene:
@generator fn linhas(caminho: string) -> string { f := open(caminho) defer f.close() // roda mesmo se o consumidor der break na linha 3 ...}É a mesma regra que a morte de processo segue (seção 8): um escopo que nunca vai ser retomado ainda deve suas limpezas. Sem isso o pipeline lazy seria um vazamento esperando pra acontecer, e o defer deixaria de ser a resposta pra “libera isso, aconteça o que acontecer”. Ser stackless é o que faz isso sair barato: como o defer é escopado por bloco, o que uma suspensão deve é sabido em tempo de compilação, então a desmontagem é código comum num ponto conhecido, não um desenrolador de runtime.
Composição de pipelines lazy
Seção intitulada “Composição de pipelines lazy”lines_from_file("log.txt") .filter(fn(l) => l.contains("ERROR")) .map(fn(l) => parse_entry(l)) .take(100)Cada estágio é um Generator[T]. Zero alocação intermediária, tudo no mesmo processo. Impossível de fazer com a mesma eficiência via processos e channels.