coolguyandClaude Opus 5 410363b230 dogfoods: 2번 Atomic File Updater — ?를 자원과 함께 쓸 수 없다
프로그램을 쓰되 돌리지 않는 방식을 시작한다. std가 원래 선언-전용이므로
실행만 빼고 전부 진짜로 검사된다 — 종이 스케치가 아니라 컴파일러가 검증한
설계다.

합격 기준을 둘로 잡았다: check exit 0 + 모듈이 실제로 해소될 것. 후자가
없으면 전자가 공허한데, 그것을 첫 시도에서 겪었다.

D1 — 상대 경로 import가 패키지로 오인됐다. is_package가 첫 세그먼트에 점이
있는지만 봐서 ".."이 걸렸다. 무서운 것은 버그가 아니라 결과였다: import가
해소되지 않으면 그 모듈의 이름이 전부 불투명해지고, "모르는 것을 틀렸다고
말하지 않는다"는 원칙에 따라 무엇이든 통과한다. 첫 check가 exit 0이었는데
없는 메서드를 불러도 통과하는 상태였다.

D2 — 값 있는 식을 문으로 버릴 수 있었다. fs.remove(path)를 문으로 쓰면
Result가 조용히 사라졌다. 즉 실패를 버리는 방법이 있었고, 내가 개밥 먹기 1차
보고서와 투어에 "이 언어에는 실패를 버릴 방법이 없다"고 적은 것은 틀렸다 —
List.each 하나의 좁은 사실을 언어 전체로 일반화했다. 이제 오류이고, 일부러
버리려면 let _ = 로 적는다. 부산물로 정리 경로의 관용구가 생겼다.

D3(본체) — ?를 자원과 함께 쓸 수 없다. naive.cool 18줄은 조기 반환으로 핸들을
누수하는데 통과한다(v0 정책). 제대로 정리한 careful.cool은 58줄이고 5단 중첩
match이며 ?를 한 번도 못 쓴다. 3.2배다. resource/with가 필요한 이유가 여기
숫자로 있다.

열어둔 것: 함수 타입에 own이 없어 고차 경계에서 소유권이 뚫린다(D5).
Mini Shell과 DB Pool이 정면으로 걸리므로 그 둘 전에 결정해야 한다.
문자 접근이 없어 어휘 분석을 못 쓴다(D6).

소유권 검사가 잡는 것은 확인했다: 두 번 닫기, 닫은 뒤 쓰기, 빌린 핸들 반환.

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

coollang

되돌리기 비싼 결정부터 확정하는 프로그래밍 언어. 현재 v0 — 설계 검증판.

$ coolc check samples/run/hello.cool
$ coolc run   samples/run/hello.cool
area = 12
area = 9
area = 3

무엇을 위한 언어인가

세 가지 목표가 나머지 모든 결정을 지배한다.

  1. 오류를 더 빨리 잡는다 — null 없음, Option/Result, 소진적 match, 확정 초기화, 선언되지 않은 effect는 컴파일 오류.
  2. 검증이 더 빠르다 — fast path / slow path 분리, 전역 추론 없음, 복잡한 trait solver 없음, 임의 매크로 없음, 인터페이스 해시 기반 무효화.
  3. 피해 범위가 좁다 — 명시적 capability, ambient authority 없음, affine 소유권.

설계의 전문은 docs/thesis.md, 문법은 docs/grammar.ebnf에 있다.

지금 되는 것

lex → parse → 이름 해소 → 타입 검사 → effect/capability 검사 → move/affinity 검사 → 소진성 검사 → interface 해시 → typed IR → 인터프리터

여섯 종류의 오류를 한국어 진단으로 보고한다: 문법, 이름, 타입, effect, capability, 소유권. 여기에 match 소진성과 두 가지 lint(미사용 import, effect 과잉 선언)가 더해진다.

$ coolc check samples/12_stdlib_effects.cool
samples/12_stdlib_effects.cool:20:14: 선언되지 않은 effect Console.print
  (leaks_effect의 effects 절은 {}입니다)

증분 검사

이 프로젝트의 중심 주장이다. 10만 줄 / 200 모듈에서 측정 (bench/bench.ml):

시간 재검사한 모듈
전체 검사 (cold) 253ms 200
함수 본문만 수정 0.8ms 1
함수 시그니처 수정 2.5ms 2

핵심은 시간이 아니라 범위다. 본문 수정이 downstream을 한 칸도 건드리지 않는 것, 그리고 시그니처 수정이 사슬 끝까지 가지 않고 해시가 변한 곳에서 멈추는 것 — 이 둘이 아키텍처의 주장 전부다.

시그니처 수정 시나리오에서는 두 모듈 건너의 match가 실제로 깨진다:

m100.cool:15:3: match가 모든 경우를 덮지 않습니다 (빠진 경우: Tri(_))
m101.cool:21:3: match가 모든 경우를 덮지 않습니다 (빠진 경우: Up.Tri(_))

실제로 써본 결과

samples/app은 검사기를 시험하려고 쓴 것이 아니라 일을 하려고 쓴 프로그램이다 — 설정 파서 + 리포트 도구, 2모듈 232줄.

$ coolc run samples/app/main.cool samples/app/example.conf
설정
  name = "coollang"   (text)
  threads = 4   (number)
  ...
항목 5개, 문제 2개

문제
  10행: 이름이 비어 있습니다
  11행: = 가 하나여야 합니다: broken = a = b

쓰면서 걸린 것들을 docs/friction.md에 남겼다. 요약하면: 되돌리기 비싼 결정은 하나도 후회되지 않았고, 불편은 전부 되돌리기 싼 것들이었다.

빌드

OCaml 5.x와 dune이 필요하다.

opam install dune
dune build
dune test          # 175개 검사
dune exec bench/bench.exe

coolc_build/default/bin/main.exe다. 설치하려면 dune install.

저장소 구성

lib/          컴파일러 (약 4,900줄 OCaml)
  lexer.ml      어휘 분석 — 문법을 하나도 모른다
  parser.ml     재귀 하강, LL(1), backtracking 없음
  resolve.ml    이름 해소 — 모듈 하나만 보고 결정할 수 있는 것
  typecheck.ml  타입 + effect + capability
  move.ml       move / affinity
  exhaust.ml    소진성 (Maranget usefulness)
  iface.ml      interface artifact + 해시
  session.ml    모듈 로딩 + 고정점 invalidation
  ir.ml         얇은 typed IR
  interp.ml     트리 워킹 인터프리터
std/          표준 라이브러리 (본문 없는 선언, 런타임이 구현)
samples/      예제 — 통과용 9개, 일부러 틀린 것 7개
bench/        증분 루프 측정

v0의 성격

이것은 쓸 수 있는 언어가 아니라 설계가 옳은지 증명된 언어다. 되돌리기 비싼 결정 — 문법, 타입, effect, capability, 소유권, 증분 아키텍처 — 이 전부 코드와 테스트로 못 박혔고, 빠른 검증 루프라는 시스템 속성이 측정으로 증명됐다.

그래서 이 코드를 통째로 버리고 v1로 번역해도 잃을 것이 없다. 애초에 그것이 v0의 목적이었다.

아직 없는 것: 진짜 컴파일(해석 실행만 한다), 병렬 실행(scope는 순차), 파일·네트워크 IO, 완전한 제네릭, 에디터 지원, 패키지 관리자. 표준 라이브러리는 len, each, map, concat, show 수준이다.

이름

언어는 coollang, 툴체인 바이너리는 coolc. 소스 확장자는 .cool.

S
Description
되돌리기 비싼 결정부터 확정하는 프로그래밍 언어. v0 — 설계 검증판.
Readme
1.1 MiB
Languages
OCaml 99.9%