Commit Graph
20 Commits
Author SHA1 Message Date
coolguyandClaude Opus 5 5ac899df68 docs: README
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 15:59:53 +09:00
coolguyandClaude Opus 5 91f3840d19 lint: 파서 오류 복구와 두 lint — 남은 부채를 턴다
파서 오류 복구:
항목 단위로만 회복한다. 문 단위로 더 잘게 회복하려 하면 파서가 추측을
하게 되고, 틀린 추측은 없는 오류를 지어낸다. 한 항목에 오류 하나가
상한이라는 것은 정직한 한계다. 동기화 지점은 중괄호 깊이 0 + 줄 첫머리
+ 선언 시작 토큰 — 셋 다 필요하다. 본문 안의 fn을 새 항목으로 오인하면
그 뒤가 전부 어긋난다. 샘플 08이 이제 오류 넷을 한 번에 보고한다.

두 lint (취향이 아니라 비용이다):
- 미사용 import는 재검사 범위를 넓힌다. 쓰지 않는 모듈의 시그니처가
  바뀌면 이 모듈이 재검사된다.
- effect 과잉 선언은 호출자에게 없는 의무를 지운다. 시그니처는 실제보다
  좁아도 안 되고 넓어도 안 된다.

과잉 선언은 effect 변수가 있거나 본문에 모르는 이름이 있으면 판정하지
않는다. 첫 구현이 샘플 03/06을 오탐으로 잡았는데, 원인이 외부 타입이었다
— 외부 capability의 메서드는 effect를 모르므로 "수행하지 않았다"고 말할
근거가 없다. saw_unknown으로 판정을 보류한다.

Resolve.error에 blocking을 나눴다. 이름 해소 실패는 뒤 단계를 막지만
lint는 막지 않는다 — lint 하나가 진짜 타입 오류를 가리면 루프가 느려진다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 15:53:35 +09:00
coolguyandClaude Opus 5 5593b54772 std: 표준 라이브러리 — effect 다형성이 처음으로 검사된다
std/list.cool, string.cool, int.cool, bool.cool. 본문 없는 선언이고
런타임이 구현한다. 이 파일들은 구현이 아니라 시험대다.

부채 상환이 아니라 검증이다. std가 없을 때 List.each는 모르는 이름이라
조용히 통과했다. "모르는 것을 틀렸다고 말하지 않는다"는 맞는 원칙이지만,
그 그늘에 검사되지 않는 영역이 숨어 있었다.

넣자마자 샘플 01이 깨졌다 — List.each에 Result를 반환하는 클로저를 넘기고
그 안에서 ?를 쓰고 있었다. each는 값을 남기지 않는 클로저만 받고, ?는
클로저 밖으로 나가지 못하며, 결과를 버릴 방법은 언어에 없다. map으로
고쳤다. 이것이 std를 먼저 한 이유 그 자체다.

- IR은 이제 모듈 그래프 전체를 받고 전역 이름은 "<경로>#<이름>"으로
  정규화된다. 별칭은 가져오는 쪽의 선택이므로 실행 의미에 남아서는 안 된다.
- 본문 없는 선언은 런타임 구현으로 낮아지고, 그 이름은 모듈 파일에서 온다
  (std/list.cool의 each = "list.each"). 별칭과 무관하다.
- prelude 없음. std도 명시적으로 가져온다.
- samples/12: effect 변수가 호출 지점에서 실제로 해소된다는 증거.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 15:47:44 +09:00
coolguyandClaude Opus 5 1c4f46e5e5 interp: 얇은 typed IR과 트리 워킹 인터프리터 — coolc run
문서에만 있던 실행 의미가 코드가 된다.

- ir.ml: AST를 얇은 IR로 낮춘다. `?`는 Result에 대한 match로 펼쳐지고,
  타입 인자는 사라지며(단형화 없음), 한정 이름은 하나의 이름으로 접힌다.
  이름은 낮추기 시점에 분류된다 — 실행 중에 "지역인가 전역인가"를 다시
  묻지 않는다.
- interp.ml: 검사하지 않는 인터프리터. 여기 도달한 프로그램은 이미 타입,
  effect, capability, ownership 검사를 통과했고, 같은 질문을 두 번 묻는
  것은 두 번째 진실을 만드는 일이다.

권한의 유일한 출처는 런타임이다. 소스에는 capability를 만드는 문법이 없고,
main은 자기가 선언한 것만 받는다. 파라미터에서 Console을 지우면 출력할
방법이 프로그램 안에 없다 — 보안 정리 (i)의 실행 시점 대응물. TaskScope의
뿌리도 같은 이유로 런타임이 준다.

scope의 v0 실행 의미는 순차다. 구조가 먼저고 병렬성은 그 위의 최적화다.

samples/run/은 이제 "검사를 통과한다"가 아니라 "이 값을 낸다"까지 말하고,
test/가 같은 것을 검사한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 14:58:05 +09:00
coolguyandClaude Opus 5 120ff361cf naming: 툴체인 바이너리를 coolc로
`cool`은 이 머신에서 이미 다른 물건이다 (프린터/리포트 멀티툴). 그 도구의
철학이 "새 도구는 서브커맨드지 별도 설치가 아니다"인데, 언어 컴파일러를
거기 얹는 것은 그 문장의 뜻이 아니다.

컴파일러 관례를 따른다 — rustc, ocamlc, tsc. 언어 이름은 coollang 그대로.
나중의 coolfmt, coolls도 같은 자리에 붙는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 14:53:16 +09:00
coolguyandClaude Opus 5 c4662ab627 bench: 증분 루프를 측정으로 증명한다
100k줄 200모듈에서 본문 수정은 downstream을 한 칸도 건드리지 않고(재검사
1개, 0.8ms), 시그니처 수정은 hash가 변한 곳까지만 전파되어(재검사 2개,
2.1ms) downstream 소진성 위반을 실제로 잡는다.

"빠르다"가 아니라 "다시 볼 것이 적다"가 주장이므로, 시간보다 재검사된
모듈 수를 먼저 출력한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 14:12:35 +09:00
coolguyandClaude Opus 5 801a7b330b modules: 모듈 경계, interface hash, 그리고 고정점 invalidation
되돌리기 비싼 결정 중 마지막 하나 — incremental 아키텍처 — 를 코드와
테스트로 닫는다.

- iface.ml: exported surface 추출과 해시. 별칭 한정(qualify)은 소비 시점에만
  일어나므로 가져오는 쪽의 별칭이 정의 모듈의 hash에 새지 않는다.
- session.ml: 모듈 로딩과 고정점 전파. hash 비교가 dependents 재검사보다
  앞선다 — 이 순서가 "본문만 수정 시 downstream 0건"의 전부다.
- 한정 이름(Alias.Type, Alias.Ctor, Alias.fn)을 타입 검사, 패턴, 소진성,
  move 검사가 모두 하나의 키("Alias.name")로 본다.
- 패키지 경로(cool.dev/std/list)는 v0에서 해소하지 않고 불투명하게 둔다.
  없다고 말하지 않는다.
- 회귀 테스트: 본문만 고치면 자기 자신만 재검사(1건), variant를 추가하면
  downstream까지 전파되고 실제로 소진성이 깨진다(2건).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 14:11:30 +09:00
coolguyandClaude Opus 5 5218bc59a7 exhaust: match exhaustiveness와 도달 불가 팔 검사
철학 1이 나열한 다섯 항목 중 비어 있던 자리를 채운다. Maranget의 usefulness
알고리즘으로 반례를 만들어 "빠진 경우"를 이름으로 말한다 — 중첩된 자리의
반례도 찾는다(Some(Rect(_, _))).

이 검사가 왜 지금 필요한가: interface hash가 enum 정의 본문을 입력으로 삼는
이유가 바로 이것이다. upstream에 variant가 하나 늘면 downstream의 match가
깨져야 하는데, 검사가 없으면 깨질 것이 없다. 다음 마일스톤(모듈 경계를 넘는
재검사)의 핵심 시나리오가 여기에 걸려 있다. 테스트로 그 시나리오를 직접
고정했다 — 같은 코드가 variant 둘일 때는 통과하고 셋이 되면 깨진다.

구현 중 한 번 틀렸다. 리터럴 패턴을 와일드카드로 줄였더니 Int 리터럴 두 개로
match가 완전해져 버렸다. 리터럴은 인자 없는 생성자이고, 타입의 생성자 집합이
무한하므로 리터럴만으로는 결코 완전해지지 않는다.

생성자 집합을 알 수 없는 타입(외부 타입, 미지수)은 검사하지 않는다.
모르는 것을 위반이라고 말하지 않는다.

definite init은 문법이 이미 보장한다는 것을 문서에 적었다 — let이 항상
초기화식을 요구하므로 별도 검사가 필요 없다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 03:23:47 +09:00
coolguyandClaude Opus 5 2e67b74376 move: move/affinity 검사 — v0 fast path 완성
보안 정리 (ii) — safe code에서 capability는 복제·위조되지 않는다 — 를 코드로
닫는다. 검사는 전부 함수 로컬 데이터플로우이고 전역 분석이 없다.

affinity의 뿌리는 capability다. 필드로 가진 타입은 전이적으로 affine이며
고정점까지 돌려 상호 재귀 타입도 유도한다. 이 전이가 없으면 wrapper 하나를
복사해 capability가 사실상 복제되므로 정리가 깨진다. copyable 선언과 affine
필드의 공존은 오류다.

구현한 규칙:
- affine 값은 소유 자리로 갈 때 move된다(own 파라미터, 반환, struct 저장,
  컨테이너 삽입, let 바인딩, by-move capture). moved 이후 사용은 오류이고
  진단이 어디서 소비됐는지를 말한다
- 분기 병합은 보수적 합집합. 한 분기에서라도 moved면 병합 이후 moved
- 빌린 값은 탈출하지 못한다: 반환, struct 저장, 소유 자리로 넘기기 전부 거부
- use의 전염: 빌린 값을 capture한 클로저는 그 자체가 빌린 값이라 소유 자리로
  갈 수 없다. 별도의 nonescaping 개념 없이 use 규칙 하나로 닫힌다
- callable affinity: affine 값을 capture한 클로저는 affine fn이며 fn 자리에
  갈 수 없다

자율 결정 둘:
- 클로저는 mut 바인딩을 capture할 수 없다. spawn만 막는 특수 규칙 대신
  일반 규칙으로 뒀다 — v0에 참조가 없으므로 별칭도 조용한 복사도 만들 수
  없고, spawn 제한은 이 규칙의 특수 사례가 된다
- v0에 부분 move는 없다. 필드 접근은 빌림이고 결과도 빌린 값이다.
  affine 필드만 꺼내려면 부분 move 상태 추적이 필요한데 v0가 살 복잡도가 아니다

05를 자족적으로 다시 썼다. affinity의 뿌리가 capability라 자원 타입을 모듈
안에서 정의해야 검사기가 affine임을 유도할 수 있다. 외부 타입은 affine임을
증명할 수 없으므로 copyable로 본다.

이로써 fast path(L0 parse / L1 type·effect·capability·ownership)가 완성됐다.
cool check가 처음으로 성공을 선언한다 — 01~04, 06, 07이 exit 0으로 통과한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 03:01:05 +09:00
coolguyandClaude Opus 5 79e4ed4190 effects: effect/capability 검사
문서가 "사활"이라고 지목한 단계다. effects 절이 여기서부터 장식이 아니라
검사 대상이 된다.

핵심 규칙 — 미선언 effect = compile error(철학 1). 수행한 자리를 들고
다니므로 진단이 함수 머리가 아니라 실제로 수행한 줄에 붙는다.

effect가 흐르는 경로 넷을 모두 막았다:
- capability 메서드 호출이 그 메서드의 선언된 effect를 요구한다
- 함수 호출이 그 함수의 effect를 물려준다
- 클로저의 effect는 정의한 자리가 아니라 부르는 자리에서 일어난다.
  클로저를 만들기만 하는 것은 effect가 아니고, 인자로 넘겨 호출되는 순간
  호출자의 것이 된다
- 파라미터가 허용한 범위를 넘는 함수를 넘기면 거부한다

effect 변수는 결정 위치에서 인자의 effect로 묶인다. 순서가 중요해서 한 번
틀렸다 — 포함 검사를 unify보다 먼저 하면 아직 해소되지 않은 미지수를 제약으로
오해해 정당한 코드를 거부한다. unify가 먼저고, 남는 차이만이 위반이다.
결정되지 않은 미지수는 판정을 미룬다 — 모르는 것을 위반이라고 말하지 않는다.

보안 정리 (i)을 직접 구현했다: capability 메서드는 값을 통해서만 부를 수
있다. 타입 이름으로 부를 수 있으면 capability 없이 effect를 수행하게 되어
정리가 무너진다.

effect 검사는 타입 검사와 같은 순회에서 돈다. effect 변수의 해소가 타입
변수와 같은 지점에서 일어나므로 떼어내면 순회와 인스턴스화를 두 번 한다.
소유는 나뉘되 순회는 하나다 — 문서에 근거를 적었다.

10_effect_errors.cool 추가. capability를 직접 정의해야 메서드의 effect가
알려지므로 외부 타입을 하나도 쓰지 않는다. 통과해야 하는 6개와 거부해야
하는 7개가 모두 의도대로 갈린다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 02:50:33 +09:00
coolguyandClaude Opus 5 5e034c0891 typecheck: 타입 검사기와 scope 부모 문법 확정
세 가지를 자율 결정으로 닫고 타입 검사까지 세웠다.

1. scope 부모 문법: scope 자식 = 부모 { ... }로 확정. 부모를 적지 않으면
   자식의 부모가 "가장 가까운 스코프"가 되는데 그것이 정확히 ambient
   authority다. 권한 사슬이 main의 루트 TaskScope부터 끊기지 않으려면 모든
   자식이 부모를 이름으로 지목해야 한다. 이름 해소가 이 구멍을 잡아준 건이라,
   주석 처리했던 중첩 예제를 되살렸다.

2. prelude: 타입 이름은 그 타입에 딸린 함수의 이름공간이다(String.len,
   File.close). 정적 메서드를 위한 별도 문법을 두지 않는다.

3. 타입 검사: 이 모듈 안에서 아는 것만 검사한다. 외부 이름은 TUnknown이
   되어 무엇과도 맞는다 — 모르는 것을 틀렸다고 말하지 않기 위해서다.
   제네릭 해소는 호출 지점의 지역 unification이고 함수 하나를 넘지 않는다.
   클로저 파라미터 타입은 기대 타입에서 읽어온다(양방향 검사, 로컬).

단계 소유권을 하나 정정했다. affinity는 타입 동등성의 일부가 아니다. 값이
affine인지는 무엇을 capture했는지로 정해지는 substructural 성질이고
move/affinity 검사가 소유한다. 타입 검사가 이걸 판정하려다 정당한 코드를
거부하는 것을 06에서 확인하고 unify에서 분리했다.

unify 버그 하나: 같은 미지수끼리 unify할 때 occurs check가 자기 자신을
발견해 실패하고 있었다. 02의 fold 호출에서 잡혔다.

09_type_errors.cool 추가 — 외부 타입이 하나도 없어 검사기가 TUnknown으로
빠져나갈 구석이 없는 파일이다. 18개 진단이 전부 잡히고, 첫 오류에서 멈추지
않고 모두 보고한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 02:42:35 +09:00
coolguyandClaude Opus 5 94e28d427b resolve: 이름 해소와 모듈 의존 표면 산출
이 단계가 답하는 질문은 하나다 — 이 모듈 하나만 보고 무엇을 결정할 수 있는가.
그 경계를 산출물로 만들었다.

결정할 수 있는 것은 오류로 보고한다: 중복 정의, 중복 파라미터·제네릭,
선언되지 않은 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
2026-08-30 02:30:52 +09:00
coolguyandClaude Opus 5 1bd88fc4d7 parser: 재귀 하강 구문 분석기와 AST
grammar.ebnf의 프로덕션 하나에 함수 하나로 대응한다. LL(1)이므로 선읽기는
항상 한 토큰이고 backtracking이 없다. 모든 노드가 위치를 들고 다닌다.

문법과 얽히는 두 자리를 원칙대로 처리했다:
- NEWLINE 흡수는 문법에 { NEWLINE }으로 적힌 자리에서만 한다. 파서가
  "여기선 줄바꿈 무시" 식으로 임의 판단하면 어디서 무시되는지 아무도 모르게
  된다.
- struct 리터럴은 if/match/scope 머리에서 금지하고(no_struct), 괄호·인자
  목록·블록에 들어가면 다시 허용한다.

문법에 새긴 제한이 실제로 파서에서 죽는 것을 확인했다. 파라미터 위치의
effect 합집합과 match 가드는 검사기가 아니라 파서가 거부하며, 진단이
원인을 직접 말한다. 후행 콤마 누락도 일반적인 "닫는 괄호 필요" 대신
"다중 줄 목록에는 후행 콤마가 필요합니다"로 보고한다.

샘플을 실제로 파싱해 두 가지를 잡았다:
- 02와 05가 own을 타입 위치에 쓰고 있었다. 확정한 규칙은 바인딩 수식어가
  이름 앞이므로 샘플이 틀렸다. 수정.
- 05에 구문 오류와 검사기 오류가 섞여 있었다. 파서가 첫 오류에서 멈추면
  검사기 케이스에 영영 도달하지 못하므로 08_syntax_errors.cool로 분리.

grammar.ebnf를 구현과 맞췄다: 제네릭 인자에 effect 집합 허용, 마지막 문의
구분자는 "}" 앞에서 생략, 중괄호 목록 안의 NEWLINE 흡수 위치 명시.

cool ast 추가. cool check는 이제 파서까지 돌리되 여전히 통과했다고 말하지
않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 02:26:01 +09:00
coolguyandClaude Opus 5 66a2cc6959 lexer: 어휘 분석 구현과 ASI 함정 하나 정정
grammar.ebnf의 어휘 절을 구현한다. 렉서는 선읽기를 요구하지 않고,
문법과 얽히는 유일한 부분인 NEWLINE 삽입은 can_end_statement 하나로
판정한다. 모든 토큰이 위치를 들고 다닌다 — 진단 품질이 헌법급이므로
나중에 붙이지 않는다.

샘플을 실제로 렉싱해 함정을 하나 잡았다. effects 절이 줄 끝에 오면 "}"가
값 종료 토큰이라 NEWLINE이 삽입되어 다음 줄의 "->"와 끊긴다(Go ASI와 같은
형태). 렉서에 문맥을 주는 대신 — 렉서 피드백은 철학 2가 배제한다 —
시그니처 머리의 흡수 위치를 프로덕션에 명시적으로 적어 닫았다. 파서가
임의로 건너뛰는 것이 아니라 문법에 적힌 자리에서만 흡수한다.
다중 줄 목록의 후행 콤마 필수도 함께 명시(콤마로 끝난 줄은 NEWLINE을
만들지 않으므로 목록이 자연히 이어진다).

cool check는 어휘 분석을 돌리되 통과했다고 말하지 않는다. 파이프라인의
나머지가 없는 이상 그 파일은 검사된 것이 아니다. 디버깅용 cool tokens 추가.

테스트 30건: 키워드, 두 글자 연산자 우선, NEWLINE 삽입 6가지 경우,
리터럴과 이스케이프, 오류 7종과 오류 위치, 그리고 samples/*.cool 전체가
어휘 분석을 통과하는지.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 02:14:37 +09:00
coolguyandClaude Opus 5 630d12104a grammar: v0 EBNF 확정과 열린 문법 결정 8건
열려 있던 문법 항목을 언어 철학에 따라 전부 닫고 EBNF로 고정한다.

- 제네릭 인자를 대괄호로 통일(List[a], map[Int, String](...)). <>는 식
  위치에서 비교 연산자와 갈리지 않아 turbofish라는 제2 표기를 부르는데,
  effect 규칙이 명시적 인스턴스화 문법을 요구하므로 회피할 수 없다.
  대괄호는 전위=리터럴 / 후위=인스턴스화로 위치가 결정해 LL(1)이고 표기가
  하나다. 대가로 인덱싱 연산자를 두지 않는다.
- 파라미터 규칙을 callable 한정에서 전 파라미터로 일반화. 기본은 빌림,
  own만 소유 이전. capability를 예외로 두지 않는 이유는 capability의 존재가
  이미 타입과 effects 절에 드러나기 때문이다 — 표기가 실을 정보는 소유 이동뿐.
- 문 구분은 줄바꿈(Go식 자동 삽입), 블록은 식, return은 조기 탈출 전용.
  if/match를 식으로 두면 재대입이 줄어 move 검사가 단순해진다.
- match 가드 없음. 가드는 exhaustiveness를 흐리고 SMT 없이는 _ 분기를
  강요한다. 철학 1의 대표 항목을 문법 편의와 바꾸지 않는다.
- ?는 Result 전용 하드코딩. 조기 탈출도 블록 종료이므로 scope의 join/cancel이
  ? 경로에서도 실행된다. v1 linear 도입 시 이 경로가 암묵 drop 문제와 만난다.
- 수식어 순서는 바인딩(own, mut) 먼저, 타입(affine) 나중.

docs/grammar.ebnf는 LL(1)을 설계 제약으로 명시하고, effect 합집합을
결과 위치 전용 프로덕션으로 새겨 파라미터 위치의 역산을 파서가 거부하게 한다.
samples/는 확정 표기로 전면 갱신.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 02:09:58 +09:00
coolguyandClaude Opus 5 e3b19b1300 thesis+samples: 표기 결정 6건 반영과 문법 탐침 추가
샘플을 먼저 써서 EBNF 이전에 체감을 확인했고, 거기서 강제된 결정을 문서에
되먹인다.

- callable 파라미터의 기본을 use(비탈출)로 뒤집고 저장하는 쪽을 own fn으로
  유표기. 반대로 두면 클로저를 저장하지 않는 고차 함수 — 표준 라이브러리의
  거의 전부 — 가 use로 도배된다. 무표기-흔함/유표기-위험은 effects 표기에
  이미 있는 원칙이라 새 조항이 아니다.
- 재수출 키워드를 reexport로 확정. export는 "처음 내보내기"와 흐려지는데
  둘은 hash 규칙에서 의미가 다르고, use는 capability 전달 전용으로 남긴다.
- effects 절을 파라미터 목록 뒤·화살표 앞으로 이동. 중첩 함수 타입의 구문
  모호성이 어순으로 사라지고 읽기 순서가 "입력과 권한 → 출력"이 된다.
  선언과 타입 표기를 같은 어순으로 통일.
- effect 위치 제한을 검사기에서 구문 수준으로 격상. 파라미터 위치는 변수
  단독 또는 리터럴 집합만, 합집합은 결과 위치 전용 프로덕션.
- scope는 이름을 갖는 형태로 확정. 중첩에서 바깥 스코프에 붙이는 정당한
  패턴이 암묵 바인딩으로는 표현되지 않는다.
- capability struct를 표준 관용구로 문서화. named effect set과 scope
  축약형은 v1 예약.

samples/는 명세가 아니라 탐침이다. 05는 통과하면 안 되는 파일이며 체커가
생기면 그대로 테스트 케이스가 된다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 01:35:30 +09:00
coolguyandClaude Opus 5 c073e18b41 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
2026-08-30 01:17:49 +09:00
coolguyandClaude Opus 5 994e2362ad thesis: move join 규칙 확정과 affine/linear 경계 명시
두 미해결 항목을 닫는다.

join 규칙 — 보수적 합집합으로 확정. 한 분기에서라도 moved면 병합 지점
이후 moved. 블록당 O(변수 수)로 결정적이고 오류가 한 줄로 끝난다.
경로별 정밀 추적은 오류 설명 가능성을 파는 거래라 채택하지 않고,
"분기 간 불일치 = 에러"는 정당한 코드까지 막아 과잉이다. 조건부 소비의
우회 강요는 인정하는 대가이며, 필요하면 v1에서 관용구로 흡수한다.

affine/linear — v0는 affine이다: 오용 금지, 누수 미검사. 따라서 v0
보안 정리의 범위는 non-duplication까지이며 자원 해제는 주장하지 않는다.
linear 검사와 해제 보장은 v1 과제로 미루되, 암묵적 drop은 선언되지 않은
effect를 실행해 철학 1과 충돌하므로 명시적 소비 강제 방향을 사전 베팅으로
남긴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 00:44:08 +09:00
coolguyandClaude Opus 5 bef741e1ba thesis: Alias/Move 모델을 v0 필수로 승격하고 incremental 규칙 정정
"일반 ownership은 v0 제외"를 폐기한다. 없는 것은 borrow checker와
lifetime이지 alias 규칙이 아니며, 최소 move 모델 없이는 "safe code에
data race 없음" 주장이 closure·channel·container 경로에서 새어나간다.

신설:
- Alias/Move 모델: copyable/affine 이분, 전 경로 move, first-class
  reference 없음. 검사는 함수 로컬 데이터플로우로 결정.
- Capability 전달: second-class use가 기본(탈출 금지), 이전 시에만 move.
  non-duplication은 복제 금지이지 재사용 금지가 아니다.
- Affinity 전이: affine 필드를 가진 타입은 자동 affine. 없으면 wrapper
  복사로 non-duplication 정리가 깨진다.
- Effect의 정적/동적 층 분리: 정적 층은 타입 수준까지, 값 identity 귀속은
  동적/툴링 층.

정정:
- interface hash 입력을 exported surface 전체의 의미적 정규형으로 재정의.
- invalidation을 "direct dependents로만 전파"에서 hash 고정점 규칙으로 교체.
  v0 측정 시나리오를 본문 수정 / 시그니처 수정 둘로 분리.
- 형식 명세 투자 범위를 affine 전이 규칙과 non-duplication까지 확장.
- v0 포함/제외 목록과 v1 계획(제한적 second-class borrow) 갱신.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 00:42:18 +09:00
coolguyandClaude Opus 5 f0739fc40c 초기 커밋: thesis 문서와 OCaml v0 스캐폴딩
문서(docs/thesis.md)에 네 가지 파생 결정을 반영:
- Effect 다형성: effect 변수는 타입 파라미터와 동일 규율(선언 명시,
  해소는 호출 지점에서 로컬). 집합 의미론으로 row polymorphism 회피.
- 인터페이스 경계: public 경계 전면 명시, 본문 추론 누출 금지,
  interface hash는 정규화된 시그니처 텍스트만으로 계산.
- 모듈/패키지: content hash는 무결성이지 가용성이 아님을 명시.
- 형식 명세 범위를 capability의 affine 규칙까지 확장.

스캐폴딩은 CLI 형태만 고정하고 파이프라인 단계는 비워 둔다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 00:35:48 +09:00