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:
+32
-18
@@ -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를 시그니처에 적는 것, 실패를 버릴 수 없는 것,
|
||||
|
||||
Reference in New Issue
Block a user