diff --git a/.claude/base/bind-system-plan.md b/.claude/base/bind-system-plan.md index cc73dfd..7dc298f 100644 --- a/.claude/base/bind-system-plan.md +++ b/.claude/base/bind-system-plan.md @@ -635,6 +635,56 @@ State는 구독 등록 자체가 안 일어남. 다이아몬드에서 중복 워 Modifier처럼 플래튼하지 않는가"는 설계 근거를 알고 싶은 사용자를 위한 좋은 심화 콘텐츠 소재. +### `:With`도 새 State 노드로 확정, 가변인자로 체인 남발 방지 (2026-08-07) + +**문제 제기(사용자)**: `:With(...)`가 문서상 가변인자 표기이긴 한데, 실제로 +호출마다(`:With(a):With(b):With(c)`처럼 체이닝할 때) 매번 새 State 노드를 +만드는 게 맞는지, 아니면 값 없이 의존성 목록만 clone-then-append로 누적하는 +가벼운 빌더로 만들어 노드 증식을 피해야 하는지가 불명확했음. + +**"빌더" 대안은 기각.** 세 가지 이유: + +1. **디버그 그래프가 꼬임.** `quad-debug`의 핵심 UX는 "무엇이 무엇에 + 연결됐는가" 그래프(`research/debug-tooling-plan.md`). With/Compute를 + 전부 실제 노드로 두면 코드상의 호출 체인이 그래프 엣지와 1:1로 그대로 + 대응됨. 빌더로 만들면 그래프 툴이 "이건 노드가 아니라 나중에 갈라지는 + 지점"이라는 가상의 분기 모양을 따로 합성해야 함 — 그럴 이유가 없음. +2. **다이아몬드 dedup을 못 타고 특수 케이스가 생김.** With가 진짜 노드면 + `w = key1:With(key2)`에서 갈라지는 `c1 = w:Compute(g1)`, `c2 = + w:Compute(g2)` 같은 흔한 fan-out이 이미 확정된 "invalid 플래그로 + 다이아몬드 중복 워크 방지" 장치(위 "전파 모델 확정" 절)를 그대로 + 재사용함. 빌더면 c1/c2가 key1/key2에 각자 직접 구독을 걸어야 해서 + 기존 dedup 경로를 매번 우회하는 특수 케이스가 생김. +3. **clone 기반 구현은 Compute 노드 위에서 실제로 깨짐(사용자 지적, + 검증 완료).** `c = a:Compute(f)` 뒤에 `w = c:With(b)`를 clone으로 + 구현하면, `table.clone`이 `c`의 캐시 슬롯(계산된 값 + `invalid` + 플래그)까지 그대로 복사해 `w`가 `c`와 별개의 독립 캐시를 갖는 사실상 + 다른 노드가 됨. `c`와 `w`가 각자 관측되면 `f`가 두 번 따로 + 실행/캐싱됨 — 바로 위 "왜 State 체인을 Modifier처럼 플래튼하지 + 않는가" 절에서 이미 기각한 것과 정확히 같은 실패 모드(공유돼야 할 + 계산이 소비자 수만큼 중복 실행). Modifier의 clone-then-append 패턴을 + State 쪽에 그대로 가져오면 안 되는 이유가 바로 이것. + +**결정**: `:With(...)`는 호출마다 self+주어진 인자들을 구독하는 **새 State +노드**를 만든다(레퍼런스 기반 구독, clone 아님) — 계산 함수는 없고 값은 +`self`를 그대로 통과(pass-through)시키되 구독 목록만 넓힌 얇은 노드. 이 +노드는 Observer와 같은 패턴(외부 weak table)으로 상위 노드의 구독자 목록에 +등록됨. + +**노드 증식 걱정은 가변인자로 해소.** 처음 문제 제기("With 하나마다 노드가 +하나씩 늘어나는 게 낭비 아니냐")는 노드 자체를 없애는 대신, `:With(...)`가 +여러 의존성을 한 번에 받을 수 있게 해서 해소함: + +- `key1:With(a, b, c):Compute(fn)` — 노드 1개(구독 3개)로 끝남. +- `key1:With(a):With(b):With(c):Compute(fn)` — 여전히 가능하지만 노드 + 3개가 만들어짐. 이건 나쁜 게 아니라 각 노드가 dedup/디버그 그래프에서 + 실제 역할(구독 fan-in 지점)을 하는 저렴한 노드(계산 없음, Modifier + clone과 같은 급의 비용)라 걱정할 비용이 아님. +- 그래도 **가변인자 스타일을 권장 관례로 삼음** — 그래프로 그릴 때도 + `:With(a, b, c)`가 `:With(a):With(b):With(c)`보다 단순(노드 1개에 들어오는 + 엣지 3개 vs 노드 3개가 순서대로 이어지는 모양)해서 디버그하기 쉬움 + (사용자 확인). + **`:With`/`:Compute` — self 인자도 lazy 핸들로 통일** - 최초안(self 값은 포지셔널 raw 값, with한 값만 클로저로 읽음)에는 실제 diff --git a/CLAUDE.md b/CLAUDE.md index e5f642c..4c9c69c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -569,3 +569,38 @@ architecture.md` 소스트리 주석 두 곳 — `Store.luau`가 여전히 옛 ` **다음 세션이 할 일**: M0 착수 전에 `pre-implementation-audit.md` 우선순위1 항목(특히 위 6개)부터 확인 — "지금 할 일" 1번 참고. `.claude/question.md` 2번에 사용자 판단이 필요한 항목 요약이 반영돼 있음. + +## 2026-08-07 세션 — `:With`도 새 State 노드로 확정 + +사용자 질문에서 시작: `:With(...)`가 문서상 가변인자 표기이긴 한데, 체이닝 +(`:With(a):With(b):With(c)`)할 때마다 실제로 새 State 노드를 만드는 게 +맞는지, 아니면 값 없이 의존성 목록만 clone-then-append로 누적하는 가벼운 +빌더로 만들어 "노드가 With 호출마다 하나씩 증가하는" 낭비를 피해야 하는지가 +불명확했음. 처음엔 "빌더" 대안(진짜 State가 아닌 clone 기반 누적 객체)을 +검토했으나, 사용자가 두 가지 반례를 직접 제시하며 기각함: + +1. **디버그 그래프가 꼬임** — `quad-debug`의 핵심 UX가 "무엇이 무엇에 + 연결됐는가" 그래프인데, With/Compute가 전부 실제 노드면 코드 호출 + 체인이 그래프 엣지와 1:1 대응되지만, 빌더로 만들면 그래프 툴이 가상의 + 분기 지점을 따로 합성해야 함. +2. **clone 기반 구현이 Compute 노드 위에서 실제로 깨짐** — `c = + a:Compute(f)` 뒤에 `w = c:With(b)`를 clone으로 구현하면 `c`의 캐시 + 슬롯까지 그대로 복사되어 `w`가 `c`와 별개의 독립 캐시를 갖게 되고, + `c`/`w`가 각자 관측되면 `f`가 두 번 따로 실행됨 — `bind-system-plan.md`가 + 이미 기각해둔 "State 체인 플래튼"과 정확히 같은 실패 모드. + +**결정**: `:With`는 호출마다 self+인자들을 레퍼런스로 구독하는 새 State +노드를 만든다(clone 아님, 계산 없는 pass-through 노드). 원래 문제 제기 +(노드 남발)는 노드를 없애는 대신 `:With(...)`를 진짜 가변인자로 만들어 +해소 — `:With(a, b, c)` 한 번으로 노드 1개(구독 3개)를 만들 수 있고, +디버그 그래프도 이쪽이 더 단순해 권장 관례로 삼음. 체이닝 스타일도 여전히 +가능하나 그건 저렴한 노드가 늘어나는 것뿐이라 문제 삼을 비용이 아님. +`base/bind-system-plan.md`의 "왜 State 체인을 Modifier처럼 플래튼하지 +않는가" 절 바로 뒤에 새 소절로 반영 완료. 다른 문서(`question.md`/ +`ROADMAP.md`/`modifier-plan.md`)엔 이 결정과 모순되거나 갱신이 필요한 +서술 없음을 확인함(감사 완료) — `modifier-plan.md`가 이미 "State가 +`:With`/`:Compute`마다 새 노드를 할당"이라고 서술해뒀던 것과도 정합적. + +다음 세션이 할 일은 안 바뀜(위 2026-08-06 네 번째 세션 절 참고) — 이 +결정은 M0 스파이크(Store/State propagation 검증)가 실제로 짜볼 때 +참고할 구체 스펙이 하나 더 생긴 것뿐.