friction: F2 취소 — else if는 원래 된다

"else if가 없어서 3~4단 중첩이 된다"고 적었는데 틀렸다. parser.ml:480이
처음부터 else 뒤의 if를 처리한다. 확인하지 않고 습관대로 중첩해 쓰고
언어를 탓한 것이다.

고쳐 쓰니 config.cool이 208줄에서 196줄이 되고 parse_line은 4단 중첩에서
평평한 5갈래가 됐다.

개밥 먹기 자체에 대한 교훈이라 문서에 남긴다. 한 사람이 쓴 300줄에서 나온
불편은 언어의 성질일 수도 있고 그 사람의 습관일 수도 있다. 확인 없이 적은
것 하나가 "심각" 등급을 달고 v1 설계 입력이 될 뻔했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
2026-08-30 16:17:30 +09:00
co-authored by Claude Opus 5
parent 5831af7760
commit 7be05be77f
4 changed files with 57 additions and 53 deletions
+32 -18
View File
@@ -1,7 +1,7 @@
# 개밥 먹기 보고 — samples/app을 쓰면서 걸린 것들
2026-08-30. coollang으로 처음 쓴 "일하는 프로그램" 하나(설정 파서 + 리포트
도구, 2모듈 304줄)에서 실제로 걸린 마찰을 적는다.
도구, 2모듈 292줄)에서 실제로 걸린 마찰을 적는다.
v0의 샘플 16개는 전부 검사기를 시험하려고 쓴 것이고, 그래서 "언어가 쓸
만한가"에 대해서는 아무것도 말해주지 않았다. 이 문서가 v0가 남기는 마지막
@@ -81,28 +81,39 @@ pub fn first_text(xs: List[String]) -> Option[String] {
> 같은 자리에서 흔한 `List.split_first(xs) -> Option[(a, List[a])]`.
> 튜플이 없으므로 마지막 것은 문법 결정을 동반한다.
### F2. `else if`가 없다 (심각)
### F2. ~~`else if`가 없다~~ — 취소. 관찰자가 틀렸다
`if`가 식이고 `else`는 식 하나를 받으므로, 문법상 `else if`가 가능해야
하는데 파서가 받지 않는다. 그래서 이렇게 된다:
처음 이 문서를 쓸 때 "`else if`가 없어서 3~4단 중첩이 된다"고 적었다.
**틀렸다.** `else if`는 처음부터 된다 (`lib/parser.ml:480`이 명시적으로
`else` 뒤의 `if`를 처리한다).
그런데도 `config.cool`을 중첩 `if`로 썼다. 언어가 강제한 것이 아니라
내가 확인하지 않고 습관대로 쓴 것이고, 그다음 언어를 탓했다.
고쳐 쓰니 208줄이 196줄이 됐고 `parse_line`은 4단에서 평평한 5갈래가
됐다:
```cool
if raw == "true" {
Flag(true)
if String.is_empty(line) {
cfg
} else if String.starts_with(line, "#") {
cfg
} else if List.len(parts) != 2 {
add_problem(cfg, no, ...)
} else if String.is_empty(String.trim(head_or(parts, ""))) {
add_problem(cfg, no, "이름이 비어 있습니다")
} else {
if raw == "false" {
Flag(false)
} else {
match Int.parse(raw) { ... }
}
add_entry(cfg, ...)
}
```
세 갈래 분기가 3단 중첩이 된다. `parse_line`은 4단까지 갔다.
**이것은 언어 결정이 아니라 파서의 빈틈으로 보인다.** 확인이 필요하다.
**개밥 먹기 자체에 대한 교훈이다.** 한 사람이 쓴 300줄에서 나온 불편은
언어의 성질일 수도 있고 그 사람의 습관일 수도 있다. 둘을 나누려면 불편을
적을 때마다 "언어가 정말 막는가"를 확인해야 한다. 확인 없이 적은 것 하나가
"심각" 등급을 달고 v1 설계 입력이 될 뻔했다.
> 제안: `else` 뒤에 블록 대신 `if` 식을 허용한다. 새 개념이 아니라
> 이미 있는 규칙(else는 식을 받는다)의 적용이다.
나머지 항목들은 확인했다 — F1은 `List.first`/`nth`가 std에 실제로 없고,
F3은 `String.join`이 실제로 없다.
### F3. `String.concat`이 2항이라 중첩 지옥이 된다 (심각)
@@ -175,9 +186,12 @@ pub copyable struct Numbered {
## 결론
v1로 넘길 때 **F1, F2, F3이 먼저다.** 다 "언어가 틀렸다"가 아니라
"없어서 우회했다"이고, 우회 비용이 코드에 그대로 보인다 — 304줄 중
70줄쯤이 이 셋 때문에 존재한다.
v1로 넘길 때 **F1 F3이 먼저다.** 다 "언어가 틀렸다"가 아니라
"std에 없어서 우회했다"이고, 우회 비용이 코드에 그대로 보인다 — 292줄 중
40줄쯤이 F1 하나 때문에 존재한다.
F2는 취소됐다. 그리고 그것이 이 문서에서 두 번째로 중요한 발견이다:
확인 없이 적은 불편 하나가 "심각" 등급을 달고 v1 설계 입력이 될 뻔했다.
반대로 **되돌리기 비싼 결정들은 하나도 후회되지 않았다.** capability를
인자로 나르는 것, effect를 시그니처에 적는 것, 실패를 버릴 수 없는 것,