// 03. TaskScope capability와 이름 있는 scope // // scope는 이름을 갖는다. 중첩 시 어느 스코프에 붙는 태스크인지가 코드에 보인다. pub fn main( use sc: TaskScope, use fs: FileSystem, use log: Logger, ) effects {TaskScope.spawn, FileSystem.read, Logger.write} { let paths = [Path("a.txt"), Path("b.txt"), Path("c.txt")] // 블록 종료 시 런타임이 자식 전원을 join한다. handle 소비에 의존하지 않는다. scope sc { List.each(paths, fn(p) { sc.spawn(fn() { let body = fs.read(p) log.write("read: ", p) }) }) } log.write("all done") } // 중첩. 이름이 있으므로 outer.spawn과 inner.spawn이 그냥 갈린다. // 안쪽 블록에서 바깥 스코프에 붙이는 것도 표현 가능하다 — 수명이 코드에 보인다. pub fn fan_out( use outer: TaskScope, use fs: FileSystem, use log: Logger, groups: List>, ) effects {TaskScope.spawn, FileSystem.read, Logger.write} { List.each(groups, fn(g) { scope inner { List.each(g, fn(p) { inner.spawn(fn() { fs.read(p) }) }) // 이 태스크는 inner가 아니라 outer의 수명을 따른다. outer.spawn(fn() { log.write("group done") }) } }) } // spawn 클로저는 by-move 또는 immutable capture만 가능하다. pub fn broadcast( use sc: TaskScope, use log: Logger, msg: String, ) effects {TaskScope.spawn, Logger.write} { scope sc { sc.spawn(fn() { log.write(msg) }) sc.spawn(fn() { log.write(msg) }) } }