app: 개밥 먹기 — 일하는 프로그램 하나와 그 마찰 보고
1단계 최소 IO: File(읽기), Args capability. 권한의 출처는 여전히 런타임 하나이고, IO 오류는 Result[a, String]이다 — 런타임이 사용자 정의 enum을 만들 수 없고, 만들 수 있게 하면 런타임이 프로그램의 타입을 알아야 한다. 2단계 std 확장: fold, filter, push, concat, reverse, is_empty, String.split/trim/starts_with/contains, Int.parse. 3단계 samples/app: 설정 파서 + 리포트 도구, 2모듈 304줄. 검사기를 시험 하려고 쓴 것이 아니라 일을 하려고 쓴 첫 프로그램이다. 산출물은 프로그램이 아니라 docs/friction.md다. 요약: - 되돌리기 비싼 결정은 하나도 후회되지 않았다. capability 전달, effect 명시, 실패를 버릴 수 없음, 소진적 match — 300줄 내내 거추장스럽지 않았고 소진성은 실제로 실수를 잡았다(Value에 경우 하나 추가하니 고칠 자리 넷을 정확히 짚었다). - 불편은 전부 되돌리기 싼 것들이었다. 리스트 n번째 접근이 없어 fold로 우회(40줄), else if가 없어 3~4단 중첩, String.concat이 2항이라 중첩 지옥. 304줄 중 70줄쯤이 이 셋 때문에 존재한다. v0의 질문은 "되돌리기 비싼 결정이 옳은가"였고 답은 그렇다이다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
@@ -3,4 +3,7 @@
|
||||
(libraries coollang)
|
||||
(deps
|
||||
(glob_files %{workspace_root}/samples/*.cool)
|
||||
(glob_files %{workspace_root}/std/*.cool)))
|
||||
(glob_files %{workspace_root}/std/*.cool)
|
||||
(glob_files %{workspace_root}/samples/app/*)
|
||||
(glob_files %{workspace_root}/samples/modules/*.cool)
|
||||
(glob_files %{workspace_root}/samples/run/*.cool)))
|
||||
|
||||
@@ -1232,3 +1232,42 @@ let () =
|
||||
in
|
||||
List.exists (fun m -> has_sub m "가져왔지만 쓰지 않습니다") ms
|
||||
&& List.exists (fun m -> has_sub m "String이(가) 필요한데 Int") ms)
|
||||
|
||||
(* ------------------------------------------------------------------ *)
|
||||
(* 실제 프로그램 *)
|
||||
(* *)
|
||||
(* samples/app은 검사기를 시험하려고 쓴 것이 아니라 일을 하려고 쓴 것이다. *)
|
||||
(* 언어가 쓸 만한지는 이런 코드에서만 드러난다. *)
|
||||
(* ------------------------------------------------------------------ *)
|
||||
|
||||
let () =
|
||||
let st = Session.create ~root:"../samples/app" ~std:"../std" () in
|
||||
match Session.run ~args:[ "../samples/app/example.conf" ] st
|
||||
"../samples/app/main.cool"
|
||||
with
|
||||
| Error e ->
|
||||
Printf.printf " (실행 오류: %s)\n" (Session.string_of_error e);
|
||||
check "app: 설정 리포트가 돈다" false
|
||||
| Ok out ->
|
||||
check "app: 항목과 문제를 센다"
|
||||
(has_sub out "항목 5개, 문제 2개");
|
||||
check "app: 값의 타입을 모양으로 정한다"
|
||||
(has_sub out "threads = 4 (number)"
|
||||
&& has_sub out "verbose = true (flag)"
|
||||
&& has_sub out "name = \"coollang\" (text)");
|
||||
check "app: 문제에 줄 번호가 붙는다"
|
||||
(has_sub out "10행: 이름이 비어 있습니다"
|
||||
&& has_sub out "11행: = 가 하나여야 합니다");
|
||||
check "app: 타입 있는 조회" (has_sub out "port = 8080")
|
||||
|
||||
let () =
|
||||
let st = Session.create ~root:"../samples/app" ~std:"../std" () in
|
||||
match Session.run st "../samples/app/main.cool" with
|
||||
| Ok out -> check "app: 인자가 없으면 말해준다" (has_sub out "경로가 필요합니다")
|
||||
| Error _ -> check "app: 인자가 없으면 말해준다" false
|
||||
|
||||
let () =
|
||||
let st = Session.create ~root:"../samples/app" ~std:"../std" () in
|
||||
match Session.run ~args:[ "/없는/파일.conf" ] st "../samples/app/main.cool" with
|
||||
| Ok out -> check "app: 없는 파일을 Err로 돌려준다" (has_sub out "오류: ")
|
||||
| Error _ -> check "app: 없는 파일을 Err로 돌려준다" false
|
||||
|
||||
Reference in New Issue
Block a user