이 단계가 답하는 질문은 하나다 — 이 모듈 하나만 보고 무엇을 결정할 수 있는가.
그 경계를 산출물로 만들었다.
결정할 수 있는 것은 오류로 보고한다: 중복 정의, 중복 파라미터·제네릭,
선언되지 않은 effect 변수, 같은 블록의 재바인딩, 불변 바인딩에 대한 대입,
모듈에 없는 reexport 대상, 지역 바인딩이 아닌 scope 이름, variant 인자 개수.
결정할 수 없는 것은 외부 참조로 기록하고 오류로 만들지 않는다. 모듈 로딩이
아직 없으므로 해소할 방법이 없고, 이 목록이 곧 모듈의 의존 표면이자
interface hash가 소비할 입력이다. cool deps로 볼 수 있다.
이름 해소가 아니면 못 하는 판정 하나를 구현했다: match의 맨 이름이 바인딩인지
인자 없는 생성자인지는 구문으로 갈리지 않는다. enum 선언에서 만든 생성자
표로 판정하고, 같은 표로 인자 개수도 검사한다.
샘플에서 실제 오류 둘을 잡았다:
- 03의 scope inner가 어디서도 오지 않는다. scope X { }의 X가 "이미 가진
TaskScope를 쓴다"인지 "새 자식 스코프를 만들어 X로 묶는다"인지가 정해지지
않은 탓이다. 후자라면 자식의 부모가 구문에 없어 ambient authority가 된다.
결정 전까지 해당 예제를 주석으로 두고 이유를 적었다.
- 03이 List를 import 없이 쓰고 있었다. 외부 참조 목록에 드러나 채웠다.
TaskScope가 use 값이라는 사실에서 검사 하나가 따라 나온다: scope의 머리는
모듈 수준 이름일 수 없고 반드시 지역 바인딩이어야 한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
61 lines
1.9 KiB
Plaintext
61 lines
1.9 KiB
Plaintext
// 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) })
|
|
}
|
|
}
|