// 03. TaskScope capability와 이름 있는 scope // // scope는 이름을 갖는다. 중첩 시 어느 스코프에 붙는 태스크인지가 코드에 보인다. import "cool.dev/std/list" as List pub fn main( sc: 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 sc { List.each(paths, fn(p) { sc.spawn(fn() { let body = fs.read(p) log.write("read: ", p) }) }) } log.write("all done") } // 중첩 — 아직 표현할 수 없다. 열린 결정 하나가 걸려 있다. // // scope X { } 의 X가 "이미 가진 TaskScope를 쓴다"인지 "새 자식 스코프를 만들어 // X로 묶는다"인지가 정해지지 않았다. 전자면 아래 inner가 어디서도 오지 않고, // 후자면 자식의 부모가 무엇인지 구문에 없다(= ambient authority). // 이름 해소가 이 구멍을 잡았다. 결정 전까지 주석으로 둔다. // // 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 { // List.each(g, fn(p) { // inner.spawn(fn() { fs.read(p) }) // }) // outer.spawn(fn() { log.write("group done") }) // } // }) // } // spawn 클로저는 by-move 또는 immutable capture만 가능하다. pub fn broadcast( sc: TaskScope, log: Logger, msg: String, ) effects {TaskScope.spawn, Logger.write} { scope sc { sc.spawn(fn() { log.write(msg) }) sc.spawn(fn() { log.write(msg) }) } }