decide(base): With도 새 State 노드로 확정, 가변인자로 체인 남발 방지
clone 기반 빌더 대안은 디버그 그래프 1:1 매핑을 깨고, Compute 노드 위에 clone하면 캐시 슬롯이 복사되어 계산이 중복 실행됨(State 체인 플래튼 기각과 같은 실패 모드). With(...)를 가변인자로 만들어 노드 남발 걱정을 해소. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
9609c7cd57
commit
ec7469f74f
2 changed files with 85 additions and 0 deletions
|
|
@ -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한 값만 클로저로 읽음)에는 실제
|
||||
|
|
|
|||
35
CLAUDE.md
35
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 검증)가 실제로 짜볼 때
|
||||
참고할 구체 스펙이 하나 더 생긴 것뿐.
|
||||
|
|
|
|||
Loading…
Reference in a new issue