// 03. TaskScope capability와 이름 있는 scope // // scope 자식 = 부모 { ... }. 부모를 구문에 적는 이유는 하나다 — // 적지 않으면 자식의 부모가 "가장 가까운 스코프"가 되고, 그것이 ambient authority다. import "cool.dev/std/list" as List pub fn main( root: TaskScope, fs: FileSystem, log: Logger, ) effects {TaskScope.spawn, FileSystem.read, Logger.write} { let paths = [Path("a.txt"), Path("b.txt"), Path("c.txt")] // 블록 종료 시 런타임이 자식 전원을 join한다. handle 소비에 의존하지 않는다. scope work = root { List.each(paths, fn(p) { work.spawn(fn() { fs.read(p) log.write("read: ", p) }) }) } log.write("all done") } // 중첩. 부모가 이름으로 지목되므로 어느 스코프에 붙는 태스크인지가 코드에 보인다. pub fn fan_out( outer: TaskScope, fs: FileSystem, log: Logger, groups: List[List[Path]], ) effects {TaskScope.spawn, FileSystem.read, Logger.write} { List.each(groups, fn(g) { scope inner = outer { 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( root: TaskScope, log: Logger, msg: String, ) effects {TaskScope.spawn, Logger.write} { scope s = root { s.spawn(fn() { log.write(msg) }) s.spawn(fn() { log.write(msg) }) } }