naming: panic을 crash로 — 그리고 이름을 고르는 원칙을 철학에 넣는다
철학 6번을 추가했다: 이름은 관례가 아니라 뜻에서 고른다 — 낯섦은 한 번 치르고 끝나지만 부정확함은 읽는 사람마다 매번 치른다. 판정 방법도 같이 적었다. 그 단어로 평범한 문장을 써 보고, 단어가 문장을 도우면 맞는 이름이고 싸우면 틀린 이름이다. "크래시는 복구하는 것이 아니라 조사하는 것이다" — 돕는다 "패닉은 복구할 수 없다" — 다른 언어에서는 할 수 있어 싸운다 panic의 자연어 뜻은 "갑작스러운 공포"다. 반응하는 쪽의 감정이지 결함에 대한 말이 아니다. 그리고 Go/Rust에서는 붙잡을 수 있어 이름이 거짓말을 한다. crash는 "계획 없이 갑자기 완전히 망가져 끝남"이고 복구의 함의가 없다 — 크래시는 복구하는 게 아니라 조사하는 것이다. 어휘의 출신도 이유가 됐다. panic+recover는 Go 전통이고 거기엔 감독이 없다. crash+supervision은 얼랭 전통이며, 우리가 만드는 것이 그쪽이다. 한국어 용어도 세 층으로 정리했다: 실패(Result) / 결함(crash) / 감독. 세 층이 세 가지 다른 기제로 규율된다 — 타입, 없음(발산), capability. "상황이 나쁨"은 결함이 아니라 실패다. 이 선을 안 그으면 crash가 게으름의 배출구가 된다. "오류"는 컴파일러 진단에만 쓴다. 얼랭 질문에 대한 답도 기록했다: 감독은 가져오고 비구조적 spawn은 안 가져온다. sc.spawn과 sup.spawn(sc, f)로 갈리며 문법 변경이 없다 — 실패를 삼키려면 Supervisor를 받았어야 하고 그것이 시그니처에 보인다. 개명은 문법을 먼저 고치고 대조 장치로 확인했다. 문장 500개, 파일 26개 모두 갈림 0건. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
+14
-14
@@ -1523,7 +1523,7 @@ let () =
|
||||
(* ------------------------------------------------------------------ *)
|
||||
|
||||
let () =
|
||||
let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_panictest" in
|
||||
let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_crashtest" in
|
||||
ignore (Sys.command (Printf.sprintf "mkdir -p %s" (Filename.quote dir)));
|
||||
let f = Filename.concat dir "m.cool" in
|
||||
let run src =
|
||||
@@ -1540,7 +1540,7 @@ let () =
|
||||
pub fn name(e: E) -> String {\n\
|
||||
\ match e {\n\
|
||||
\ A => \"a\",\n\
|
||||
\ B => panic(\"B는 아직\"),\n\
|
||||
\ B => crash(\"B는 아직\"),\n\
|
||||
\ }\n\
|
||||
}\n\n\
|
||||
test \"통과\" {\n\
|
||||
@@ -1549,7 +1549,7 @@ let () =
|
||||
test \"assert 실패\" {\n\
|
||||
\ Test.assert(1 == 2)\n\
|
||||
}\n\n\
|
||||
test \"panic 실패\" {\n\
|
||||
test \"crash 실패\" {\n\
|
||||
\ Test.assert(name(B) == \"b\")\n\
|
||||
}"
|
||||
in
|
||||
@@ -1558,9 +1558,9 @@ let () =
|
||||
List.iter
|
||||
(fun (e : Session.error) -> Printf.printf " (%s)\n" e.message)
|
||||
es;
|
||||
check "panic/test 예제가 검사를 통과한다" false
|
||||
check "crash/test 예제가 검사를 통과한다" false
|
||||
| Ok rs ->
|
||||
check "panic/test 예제가 검사를 통과한다" true;
|
||||
check "crash/test 예제가 검사를 통과한다" true;
|
||||
check "테스트 셋이 잡힌다" (List.length rs = 3);
|
||||
let failed =
|
||||
List.filter (fun (r : Interp.test_result) -> r.t_failure <> None) rs
|
||||
@@ -1576,10 +1576,10 @@ let () =
|
||||
| Some (_, m) -> has_sub m "assert 실패"
|
||||
| None -> false)
|
||||
rs);
|
||||
check "panic 메시지가 그대로 보고된다"
|
||||
check "crash 메시지가 그대로 보고된다"
|
||||
(List.exists
|
||||
(fun (r : Interp.test_result) ->
|
||||
r.t_name = "panic 실패"
|
||||
r.t_name = "crash 실패"
|
||||
&&
|
||||
match r.t_failure with
|
||||
| Some (_, m) -> has_sub m "B는 아직"
|
||||
@@ -1594,7 +1594,7 @@ let () =
|
||||
|
||||
(* 테스트는 effect를 수행할 수 없다 — capability를 받지 않으므로 증명된다 *)
|
||||
let () =
|
||||
let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_panictest" in
|
||||
let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_crashtest" in
|
||||
let f = Filename.concat dir "e.cool" in
|
||||
write f
|
||||
"pub capability Db {\n\
|
||||
@@ -1625,7 +1625,7 @@ let () =
|
||||
check "samples/app의 테스트가 전부 통과한다"
|
||||
(List.for_all (fun (r : Interp.test_result) -> r.t_failure = None) rs)
|
||||
|
||||
(* panic은 scope 트리를 타고 올라간다. 자식이 panic하면 그 scope는 실패하고,
|
||||
(* crash는 scope 트리를 타고 올라간다. 자식이 crash하면 그 scope는 실패하고,
|
||||
scope 뒤의 코드는 실행되지 않는다.
|
||||
|
||||
v0는 태스크가 하나라 이 규칙이 축약된 형태로만 관측된다. 아래에서 "자식
|
||||
@@ -1640,16 +1640,16 @@ let () =
|
||||
\ c.print(\"scope 앞\")\n\
|
||||
\ scope work = root {\n\
|
||||
\ work.spawn(fn() { c.print(\"자식 하나\") })\n\
|
||||
\ work.spawn(fn() { panic(\"자식 둘이 죽었다\") })\n\
|
||||
\ work.spawn(fn() { crash(\"자식 둘이 죽었다\") })\n\
|
||||
\ }\n\
|
||||
\ c.print(\"scope 뒤\")\n\
|
||||
}")
|
||||
with
|
||||
| out, Some e ->
|
||||
check "자식의 panic이 scope 밖으로 전파된다" (has_sub e.message "자식 둘이 죽었다");
|
||||
check "panic 전의 출력은 남는다" (has_sub out "자식 하나");
|
||||
check "자식의 crash가 scope 밖으로 전파된다" (has_sub e.message "자식 둘이 죽었다");
|
||||
check "crash 전의 출력은 남는다" (has_sub out "자식 하나");
|
||||
check "scope 뒤는 실행되지 않는다" (not (has_sub out "scope 뒤"))
|
||||
| _, None ->
|
||||
check "자식의 panic이 scope 밖으로 전파된다" false;
|
||||
check "panic 전의 출력은 남는다" false;
|
||||
check "자식의 crash가 scope 밖으로 전파된다" false;
|
||||
check "crash 전의 출력은 남는다" false;
|
||||
check "scope 뒤는 실행되지 않는다" false
|
||||
|
||||
Reference in New Issue
Block a user