thesis: second-class 값 개념으로 use/callable/spawn을 일괄 정리
관통하는 해법 하나로 다섯 항목을 닫는다 — second-class 값의 일반화. - Capability 전달: use의 탈출을 capture 전면 금지가 아니라 "use의 전염"으로 해결한다. use 값을 capture한 closure는 그 자체가 use 값이 되고 use 파라미터 위치로만 전달된다. 저장하는 sink는 일반 fn을 요구하므로 대입이 거부되고, 판정 정보가 전부 시그니처에 남아 검사는 로컬로 유지된다. 별도의 nonescaping 개념을 만들지 않아 "한 개념 한 방식"이 지켜진다. - Affinity 전이를 closure 환경까지 확장: callable은 fn(copyable)과 affine fn 두 종류이며, affinity는 타입 표기의 일부로 interface hash에 포함된다. bound method와 partial application도 같은 규칙. - Effect 다형성: 합집합이 있는 이상 일반 unification이 아니므로 해소를 pattern unification으로 제한한다. 결정 위치에 단독 등장, 합집합은 결과 위치 전용이며 분해하지 않음. 역산 시그니처는 작성 단계에서 거부된다. - Invalidation 순서 정정: 변경 모듈 재검사 → hash 비교 → 달라졌을 때만 dependents를 큐에 추가. hash 비교가 dependents 재검사보다 앞서야 "본문만 수정 시 downstream 0건"이 성립한다. interface hash의 re-export 입력도 목록이 아니라 해소된 정의 본문으로 정정. - 동시성: spawn은 primitive가 아니라 TaskScope capability의 메서드다. TaskScope는 use-only라 탈출할 수 없고, join/cancel은 handle 소비 관례가 아니라 scope 구문의 실행 의미로 강제한다. v0가 미소비를 허용하는 이상 handle 기반 보장은 불가능하기 때문이다. 루트 TaskScope는 main에 주입. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
+47
-12
@@ -57,9 +57,14 @@ Capability 전달 (Alias/Move 모델의 제3의 규칙):
|
|||||||
capability는 second-class "use" 전달을 기본으로 한다.
|
capability는 second-class "use" 전달을 기본으로 한다.
|
||||||
- 기본은 use(호출 동안의 임시 접근): 호출자는 호출 후에도 계속 보유하고,
|
- 기본은 use(호출 동안의 임시 접근): 호출자는 호출 후에도 계속 보유하고,
|
||||||
피호출자는 사용만 할 수 있다
|
피호출자는 사용만 할 수 있다
|
||||||
- use로 받은 capability는 탈출 불가: 반환 금지, struct 저장 금지,
|
- use로 받은 capability는 탈출 불가: 반환 금지, struct 저장 금지, channel 전송 금지
|
||||||
channel 전송 금지, 호출보다 오래 사는 closure에 capture 금지
|
- use는 전염된다: use 값을 capture한 closure는 그 자체가 use 값이 되고,
|
||||||
※ 탈출 검사는 함수 로컬로 결정 가능
|
use 값은 use로 표시된 파라미터 위치로만 전달할 수 있다
|
||||||
|
※ closure를 저장하는 sink는 일반 fn 파라미터를 요구하므로 use-closure를 넘길 수
|
||||||
|
없다 → compile error. 판정에 필요한 정보가 전부 시그니처에 있고 검사는 로컬
|
||||||
|
※ capture 전면 금지는 with_retry(fn(){ pay.refund() }) 같은 정당한 패턴까지
|
||||||
|
죽이므로 기각. 별도의 nonescaping 개념을 만들지 않고 use의 전염으로 정의해
|
||||||
|
"한 개념 한 방식"을 지킨다 (Swift @noescape, second-class values의 검증된 경로)
|
||||||
- 소유권 이전이 필요할 때만 명시적 move
|
- 소유권 이전이 필요할 때만 명시적 move
|
||||||
근거: non-duplication은 "복제 금지"이지 "재사용 금지"가 아니다. use는 복제를
|
근거: non-duplication은 "복제 금지"이지 "재사용 금지"가 아니다. use는 복제를
|
||||||
만들지 않으므로 보안 정리와 양립하고, 탈출 금지가 locality를 보존한다.
|
만들지 않으므로 보안 정리와 양립하고, 탈출 금지가 locality를 보존한다.
|
||||||
@@ -69,8 +74,16 @@ Affinity 전이 (보안 주장의 필수 전제):
|
|||||||
컴파일러가 타입 정의에서 유도하며 전이적으로 적용된다
|
컴파일러가 타입 정의에서 유도하며 전이적으로 적용된다
|
||||||
- copyable 선언과 affine 필드의 공존은 compile error
|
- copyable 선언과 affine 필드의 공존은 compile error
|
||||||
- 타입의 affinity는 exported interface surface의 일부이며 interface hash에 포함된다
|
- 타입의 affinity는 exported interface surface의 일부이며 interface hash에 포함된다
|
||||||
근거: 이 규칙이 없으면 Box{pay}를 복사하는 것만으로 capability가 사실상 복제되어
|
- 이 전이는 closure 환경에도 동일하게 적용된다 (callable affinity):
|
||||||
non-duplication 정리가 깨진다.
|
v0의 callable은 두 종류다 —
|
||||||
|
fn(...) copyable. copyable·immutable 환경만 capture 가능
|
||||||
|
affine fn(...) affine 값을 소유할 수 있고, 자신도 affine이라 move 규칙을 따름
|
||||||
|
affine 값을 capture한 closure의 타입은 자동으로 affine fn이 되며 fn 위치에 대입 불가.
|
||||||
|
affinity는 callable 타입 표기의 일부로 공개 시그니처와 interface hash에 포함된다
|
||||||
|
※ bound method와 partial application도 "환경을 보관하는 값"이므로 같은 규칙
|
||||||
|
※ use-closure(위 Capability 전달)는 affinity와 직교하는 파라미터 위치 속성이다
|
||||||
|
근거: 이 규칙이 없으면 Box{pay}를 복사하거나 affine 값을 capture한 closure를
|
||||||
|
복사하는 것만으로 capability가 사실상 복제되어 non-duplication 정리가 깨진다.
|
||||||
|
|
||||||
동시성 (철학 1,2,3에서 파생 — Alias/Move 모델의 특수 사례):
|
동시성 (철학 1,2,3에서 파생 — Alias/Move 모델의 특수 사례):
|
||||||
- green thread (async/await의 function coloring은 철학 5 위반)
|
- green thread (async/await의 function coloring은 철학 5 위반)
|
||||||
@@ -79,7 +92,16 @@ non-duplication 정리가 깨진다.
|
|||||||
공유는 deep immutable만 (borrow checker는 complexity budget 초과)
|
공유는 deep immutable만 (borrow checker는 complexity budget 초과)
|
||||||
- spawn closure는 by-move capture 또는 immutable capture만 허용.
|
- spawn closure는 by-move capture 또는 immutable capture만 허용.
|
||||||
mutable 참조 capture는 문법적으로 금지
|
mutable 참조 capture는 문법적으로 금지
|
||||||
- spawn은 effect로 선언 → 동시성이 시그니처에 드러남
|
- spawn은 primitive가 아니라 TaskScope capability의 메서드다.
|
||||||
|
effect spawn은 아래 "정적/동적 층 분리"대로 그 타입에 묶인다
|
||||||
|
(PaymentGateway.refund와 동형)
|
||||||
|
- TaskScope는 use-only(second-class)라 블록 밖으로 탈출할 수 없다
|
||||||
|
— Capability 전달의 use 메커니즘을 그대로 재사용한다
|
||||||
|
- join/cancel은 handle 소비 관례가 아니라 scope 구문의 실행 의미로 강제한다:
|
||||||
|
scope { ... } 블록 종료 시 런타임이 자식 전원을 join/cancel한다
|
||||||
|
※ v0는 affine 미소비(누수)를 허용하므로 handle 기반 보장은 애초에 불가능하다.
|
||||||
|
실행 의미로 옮겨야 그 허점이 사라진다
|
||||||
|
- 루트 TaskScope는 main에 주입 → ambient authority 없이 권한 사슬이 main부터 닫힌다
|
||||||
|
|
||||||
Effect 다형성 (철학 2,5에서 파생):
|
Effect 다형성 (철학 2,5에서 파생):
|
||||||
- effect 변수는 타입 파라미터와 정확히 같은 규율을 따른다:
|
- effect 변수는 타입 파라미터와 정확히 같은 규율을 따른다:
|
||||||
@@ -91,7 +113,16 @@ Effect 다형성 (철학 2,5에서 파생):
|
|||||||
※ 전역 추론이 배제하는 것은 "선언 없이 프로그램 전체를 보고 알아내기"이지
|
※ 전역 추론이 배제하는 것은 "선언 없이 프로그램 전체를 보고 알아내기"이지
|
||||||
선언된 변수의 로컬 해소가 아니다. map 체크에 필요한 것은
|
선언된 변수의 로컬 해소가 아니다. map 체크에 필요한 것은
|
||||||
map과 인자의 시그니처뿐 (locality 유지)
|
map과 인자의 시그니처뿐 (locality 유지)
|
||||||
- effect는 순서 없는 집합, 변수는 집합 변수, 합성은 합집합 → 결정 가능·로컬
|
- effect는 순서 없는 집합, 변수는 집합 변수, 합성은 합집합
|
||||||
|
- 합집합이 있는 이상 일반 unification이 아니다 (해가 유일하지 않다).
|
||||||
|
v0는 해소를 pattern unification으로 제한해 유일성과 선형 비용을 산다:
|
||||||
|
· effect 변수는 어떤 파라미터의 effect 자리에 "단독으로" 등장해야 한다(결정 위치)
|
||||||
|
· 변수의 해는 그 결정 위치에서 읽어오기만 한다
|
||||||
|
· 합집합 식은 결과 위치에만 허용하며 절대 분해하지 않는다
|
||||||
|
· 합집합 식이 기대 타입과 대조되는 위치는 compile error + 명시적 인스턴스화 요구
|
||||||
|
※ map·filter·fold류는 전부 이 형태에 들어간다. 역산이 필요한 시그니처(peel류)는
|
||||||
|
작성 자체가 거부된다 — 표현력을 팔아 결정성을 사는 거래이고,
|
||||||
|
이 언어에서는 항상 그 방향이 맞다
|
||||||
- row polymorphism 불필요: 차집합("이 effect만 빼고")이 필요해지는 것은
|
- row polymorphism 불필요: 차집합("이 effect만 빼고")이 필요해지는 것은
|
||||||
handler를 넣을 때이고, 그것은 v0 범위 밖
|
handler를 넣을 때이고, 그것은 v0 범위 밖
|
||||||
- 완화 장치 하나만 예약: 클로저 인자의 effects 생략 시 {}로 기본 해석
|
- 완화 장치 하나만 예약: 클로저 인자의 effects 생략 시 {}로 기본 해석
|
||||||
@@ -115,15 +146,19 @@ interface hash의 입력 = 모듈 exported surface 전체의 의미적 정규형
|
|||||||
- 타입의 affinity (copyable / affine)
|
- 타입의 affinity (copyable / affine)
|
||||||
- exported constant의 타입과 값
|
- exported constant의 타입과 값
|
||||||
- capability/effect 선언
|
- capability/effect 선언
|
||||||
- re-export 목록
|
- re-export된 선언을 완전히 해소한 정의 본문 (목록이 아니다)
|
||||||
|
※ A의 enum에 variant가 추가되면 B의 소스가 그대로여도 B의 hash가 변하고,
|
||||||
|
C의 exhaustive match가 재검사된다. "의미적 정규형"을 구현 수준까지 내린 것
|
||||||
원칙: downstream 검사 결과에 영향을 줄 수 있는 모든 것을 포함한다. 의심스러우면
|
원칙: downstream 검사 결과에 영향을 줄 수 있는 모든 것을 포함한다. 의심스러우면
|
||||||
넣는다 — 과잉 포함의 비용은 재검사지만 누락의 비용은 잘못된 캐시라는 비대칭.
|
넣는다 — 과잉 포함의 비용은 재검사지만 누락의 비용은 잘못된 캐시라는 비대칭.
|
||||||
|
|
||||||
invalidation 전파 = 고정점 규칙 ("direct dependents로만 전파"가 아니다):
|
invalidation 전파 = 고정점 규칙 ("direct dependents로만 전파"가 아니다):
|
||||||
1. 변경된 모듈의 direct dependents를 재검사
|
1. 변경된 모듈 자체를 재검사
|
||||||
2. 그중 interface hash가 실제로 바뀐 모듈만 다음 단계로 전파
|
2. 재검사 전후의 interface hash를 비교
|
||||||
3. hash가 안정되는 지점에서 중단
|
3. 달라졌을 때만 그 모듈의 dependents를 큐에 추가
|
||||||
※ implementation-only 변경은 1단계에서 멈춘다 (hash 불변 → 전파 0)
|
4. 큐가 빌 때까지(= hash가 안정되는 고정점까지) 1~3 반복
|
||||||
|
※ 순서가 중요하다. dependents를 먼저 재검사하면 "본문만 수정 시 downstream 0건"이
|
||||||
|
성립하지 않는다 — hash 비교가 dependents 재검사보다 앞서야 한다
|
||||||
|
|
||||||
Generics (철학 2에서 파생):
|
Generics (철학 2에서 파생):
|
||||||
- 기본 shared implementation, monomorphization은 명시적 요청 시만
|
- 기본 shared implementation, monomorphization은 명시적 요청 시만
|
||||||
|
|||||||
Reference in New Issue
Block a user