research: Blocker 프리미티브 채택 확정 — Batch(lexical) 대안, 문서 마무리
세션 마무리 라운드. Batch를 부활시킨 게 아니라 완전히 별개인 새 primitive Blocker를 채택한 것으로 명확히 분리: - Batch(함수/코루틴 스코프 lexical block)는 그대로 기각 유지, 반면교사 기록. - Blocker: 콜스택/코루틴이 아니라 값(On/Off)으로 지연 구간을 표현해 코루틴 yield 위험을 구조적으로 우회. state:Block(blocker)->state가 호출 즉시 onunblock 핸들을 등록(지연 등록 아님, 사용자 정정 반영). 이름 확정 (Blocker/On/Off/IsBlocked/HasBlockedEmit). 재진입(네스팅)은 의도적으로 미지원 — Rust poisoned-mutex류 위험 회피, 문서화 강조 필수로 기록. 사용 가이드: 파이프라인 최종 연산 지점에 배치. documentation-content-map.md에 새 심화/quadnomicon 콘텐츠 후보 반영: State 파생 체인 동작 원리, :Compute의 조건부 의존값 사용 팁, Blocker 사용 가이드(네스팅 금지 최우선 강조), "왜 Batch 대신 Blocker인가" 비교 에세이, push-invalidate/pull-recompute의 laziness 설계 철학 심층 에세이. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
4358fa71d7
commit
ccde1cb31c
3 changed files with 192 additions and 45 deletions
|
|
@ -10,16 +10,16 @@
|
||||||
|
|
||||||
## 지금 열려있는 것 (우선순위순)
|
## 지금 열려있는 것 (우선순위순)
|
||||||
|
|
||||||
### 0. 추가 프리미티브 필요성 — 사용자 요청, 대부분 수렴(2026-08-06)
|
### 0. 추가 프리미티브 필요성 — 사용자 요청, 대부분 수렴(2026-08-06~07)
|
||||||
|
|
||||||
사용자 질문: "다른 독립 프리미티브나 종속 파생 데이터는 뭐가 더 필요할 것
|
사용자 질문: "다른 독립 프리미티브나 종속 파생 데이터는 뭐가 더 필요할 것
|
||||||
같나요. 이것만으로 이 프로젝트는 충분하다 생각해요?" — 여러 서브에이전트
|
같나요. 이것만으로 이 프로젝트는 충분하다 생각해요?" — 여러 서브에이전트
|
||||||
조사 + 사용자와 라이브 논의로 계속 수렴 중, 상세는 `research/
|
조사 + 사용자와 라이브 논의로 계속 수렴 중, 상세는 `research/
|
||||||
additional-primitives-plan.md`. 요지:
|
additional-primitives-plan.md`. 요지:
|
||||||
|
|
||||||
- **키 기반 동적 컬렉션 재조정(유일하게 아직 열려있음, 최우선)**: Fusion
|
- **키 기반 동적 컬렉션 재조정(유일하게 아직 완전히 열려있음, 최우선)**:
|
||||||
`ForPairs`/`ForKeys`/`ForValues`, Vide `indexes()`/`values()`, React
|
Fusion `ForPairs`/`ForKeys`/`ForValues`, Vide `indexes()`/`values()`,
|
||||||
`key` prop에 대응하는 프리미티브가 quad엔 전혀 없음 확인 —
|
React `key` prop에 대응하는 프리미티브가 quad엔 전혀 없음 확인 —
|
||||||
`pre-implementation-audit.md` 1-7번(Slot CRUD 미정의)과 같은 지점이라
|
`pre-implementation-audit.md` 1-7번(Slot CRUD 미정의)과 같은 지점이라
|
||||||
같이 정의해야 함. **자유 함수**(plain data 또는 State 둘 다 받는
|
같이 정의해야 함. **자유 함수**(plain data 또는 State 둘 다 받는
|
||||||
폴리모픽 시그니처, State 메소드 프레이밍은 Source 안 쓰는 컴포넌트가
|
폴리모픽 시그니처, State 메소드 프레이밍은 Source 안 쓰는 컴포넌트가
|
||||||
|
|
@ -30,10 +30,21 @@ additional-primitives-plan.md`. 요지:
|
||||||
단순 primitive(재실행 개념 없음, Observer와 별개)로 합의, 시그니처만
|
단순 primitive(재실행 개념 없음, Observer와 별개)로 합의, 시그니처만
|
||||||
남음. Observer에 cleanup 반환 계약을 얹는 안은 기각(클로저 업밸류로
|
남음. Observer에 cleanup 반환 계약을 얹는 안은 기각(클로저 업밸류로
|
||||||
이미 충분 — `pre-implementation-audit.md` 3-1과 같은 논리).
|
이미 충분 — `pre-implementation-audit.md` 3-1과 같은 논리).
|
||||||
- **Batch/Transaction — 프리미티브로 안 만들기로 결정 완료**: lexical
|
- **Blocker — 채택, 핵심 메커니즘+이름 확정(2026-08-07 신설)**: 여러
|
||||||
|
Source를 한꺼번에 바꿔도 파생값 재계산/재대입이 한 번만 되게 하는
|
||||||
|
primitive — lexical Batch(아래 항목, 기각)와 달리 콜스택/코루틴이 아니라
|
||||||
|
**값**(`Blocker` 객체의 `On()`/`Off()`)으로 지연 구간을 표현해 코루틴
|
||||||
|
yield 위험을 구조적으로 우회함. `state:Block(blocker) -> state`가
|
||||||
|
gated state를 반환(호출 즉시 onunblock 핸들 등록), `IsBlocked`/
|
||||||
|
`HasBlockedEmit` 필드, 재진입(네스팅)은 의도적으로 미지원(Rust
|
||||||
|
poisoned-mutex류 위험 회피 — 겹치는 배치는 각자 새 `Blocker`를 쓸 것,
|
||||||
|
**문서화에서 강하게 명시 필요**). `base/`로 승격 가능한 수준, 남은 건
|
||||||
|
문서화뿐.
|
||||||
|
- **Batch(함수/코루틴 스코프 lexical block) — 기각 확정**: lexical
|
||||||
transaction 블록이 코루틴 yield 위에서 구조적으로 위험(전역/코루틴
|
transaction 블록이 코루틴 yield 위에서 구조적으로 위험(전역/코루틴
|
||||||
스코프 플래그 둘 다 새 코루틴 스폰이나 영구 yield에 깨짐) — 대신
|
스코프 플래그 둘 다 새 코루틴 스폰이나 영구 yield에 깨짐) — Blocker로
|
||||||
심화 최적화 팁 + quadnomicon "왜 없는가" 에세이로 문서화.
|
대체됐으므로 더 이상 미해결 문제 아님, quadnomicon "왜 Batch 대신
|
||||||
|
Blocker인가" 에세이 대상.
|
||||||
- **Context — 기각 확정, 대안이던 레이어드 Store도 철회**: 완전 자동
|
- **Context — 기각 확정, 대안이던 레이어드 Store도 철회**: 완전 자동
|
||||||
버전은 Roblox Luau 플랫폼 한계로 사실상 불가, 얕은 버전도 Slot 비동기
|
버전은 Roblox Luau 플랫폼 한계로 사실상 불가, 얕은 버전도 Slot 비동기
|
||||||
추가에서 조용히 깨짐 + quad-debug 철학과 충돌. 레이어드 Store 대안은
|
추가에서 조용히 깨짐 + quad-debug 철학과 충돌. 레이어드 Store 대안은
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
# 추가 프리미티브 필요성 조사 — 다른 프레임워크 대비 갭 분석
|
# 추가 프리미티브 필요성 조사 — 다른 프레임워크 대비 갭 분석
|
||||||
|
|
||||||
**상태**: research — 사용자와 라이브 논의로 계속 수렴 중(2026-08-06). Context/
|
**상태**: research — 사용자와 라이브 논의로 수렴(2026-08-06~07). Context/
|
||||||
Batch는 **결정 완료**(둘 다 프리미티브로 안 만듦). 키 기반 컬렉션 재조정은
|
Batch(lexical)는 **기각 확정**. **Blocker**(새 primitive, Batch의 대안으로
|
||||||
설계 진행 중(사용자가 "작업 전에 모든 정의를 마치고 싶다"고 명시 — M0 전
|
채택)는 핵심 메커니즘+이름 확정, 문서화만 남음. 키 기반 동적 컬렉션
|
||||||
완전 확정이 목표). Effect는 거의 수렴, 시그니처만 남음.
|
재조정은 설계 진행 중(사용자가 "작업 전에 모든 정의를 마치고 싶다"고
|
||||||
|
명시 — M0 전 완전 확정이 목표). Effect는 거의 수렴, 시그니처만 남음.
|
||||||
|
|
||||||
## 배경
|
## 배경
|
||||||
|
|
||||||
|
|
@ -28,7 +29,8 @@ Vide/v1/artworks 소스 근거 조사, Context 구현 난이도 판정) + 그
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 키 기반 동적 컬렉션 재조정 | **진짜 빈 자리, 최우선** | 설계 진행 중 |
|
| 키 기반 동적 컬렉션 재조정 | **진짜 빈 자리, 최우선** | 설계 진행 중 |
|
||||||
| Effect(leaf 죽음에 확정 정리) | 진짜 빈 자리 | 거의 수렴, 시그니처만 남음 |
|
| Effect(leaf 죽음에 확정 정리) | 진짜 빈 자리 | 거의 수렴, 시그니처만 남음 |
|
||||||
| Batch/Transaction | **프리미티브로 안 만듦** — 문서화로 대체 | 결정 완료 |
|
| **Blocker(값 기반 emit 지연/합치기)** | **채택** — Batch의 대안 | 핵심 메커니즘+이름 확정, 문서화만 남음 |
|
||||||
|
| Batch(함수/코루틴 스코프 lexical block) | **기각** | 결정 완료 |
|
||||||
| Context(트리 하위 암묵 전파) | **기각** — 대안(레이어드 Store)도 철회 | 결정 완료 |
|
| Context(트리 하위 암묵 전파) | **기각** — 대안(레이어드 Store)도 철회 | 결정 완료 |
|
||||||
| Observer에 cleanup 반환 계약 추가 | **기각** — 클로저 업밸류로 이미 충분 | 결정 완료 |
|
| Observer에 cleanup 반환 계약 추가 | **기각** — 클로저 업밸류로 이미 충분 | 결정 완료 |
|
||||||
| Untrack/Peek | 빈 자리 아님 | - |
|
| Untrack/Peek | 빈 자리 아님 | - |
|
||||||
|
|
@ -192,7 +194,13 @@ table로 되는 Observer보다 비쌈) — 사용자가 필요할 때만 쓰는
|
||||||
**상태**: 사실상 수렴 — 시그니처(`fn`의 인자 유무, `EffectHandle`의 모양)만
|
**상태**: 사실상 수렴 — 시그니처(`fn`의 인자 유무, `EffectHandle`의 모양)만
|
||||||
다듬으면 `base/`로 승격 가능해 보임. 계속 논의 원하면 이어감.
|
다듬으면 `base/`로 승격 가능해 보임. 계속 논의 원하면 이어감.
|
||||||
|
|
||||||
## 3. Batch/Transaction — 프리미티브로 안 만듦 (결정 완료)
|
## 3. Batch(함수/코루틴 스코프 lexical block) — 기각 확정
|
||||||
|
|
||||||
|
**중요**: 아래는 "값 기반 지연/합치기"라는 문제 자체를 기각한 게 아니라,
|
||||||
|
**그 문제를 lexical block(Solid `batch()`/MobX `runInAction()`류)으로
|
||||||
|
풀려는 접근만** 기각한 것이다. 실제 해법은 완전히 다른 별개 primitive인
|
||||||
|
**Blocker**(아래 3-1번)로 채택됨 — 이 절은 "왜 lexical 접근은 안 되는가"만
|
||||||
|
다루는 순수 반면교사 기록으로 남긴다.
|
||||||
|
|
||||||
### "즉시 pull"이 뭔지 (참고용 예시)
|
### "즉시 pull"이 뭔지 (참고용 예시)
|
||||||
|
|
||||||
|
|
@ -209,7 +217,7 @@ b:Set(2) -- total 무효화 → 핸들러가 즉시 (av=1, bv=2)로 다시 재
|
||||||
-- BackgroundColor3가 중간값을 한 번 거쳐가고, 두 번 대입됨
|
-- BackgroundColor3가 중간값을 한 번 거쳐가고, 두 번 대입됨
|
||||||
```
|
```
|
||||||
|
|
||||||
### 왜 lexical block 방식(Solid `batch()`/MobX `runInAction()`)을 안 쓰는가
|
### 왜 lexical block 방식을 안 쓰는가
|
||||||
|
|
||||||
`Batch(fn)`을 "플래그 세우고 fn 실행, 끝나면 flush"로 구현하면 **fn이
|
`Batch(fn)`을 "플래그 세우고 fn 실행, 끝나면 flush"로 구현하면 **fn이
|
||||||
yield하는 순간 위험해진다**(사용자 지적, 정확함):
|
yield하는 순간 위험해진다**(사용자 지적, 정확함):
|
||||||
|
|
@ -224,20 +232,108 @@ yield하는 순간 위험해진다**(사용자 지적, 정확함):
|
||||||
pull보다 더 나쁜 실패 모드.
|
pull보다 더 나쁜 실패 모드.
|
||||||
|
|
||||||
이건 구현을 잘 짜서 피할 수 있는 버그가 아니라 **lexical block 모델
|
이건 구현을 잘 짜서 피할 수 있는 버그가 아니라 **lexical block 모델
|
||||||
자체가 협조적 스케줄링 환경과 구조적으로 안 맞는 것**으로 판단.
|
자체가 협조적 스케줄링 환경과 구조적으로 안 맞는 것**으로 판단, 기각.
|
||||||
|
|
||||||
### 결정: 프리미티브 없음, 대신 문서화 두 갈래
|
## 3-1. Blocker — 채택된 새 primitive (Batch의 대안, 2026-08-06~07 세션)
|
||||||
|
|
||||||
1. **심화(사용자 대상) 최적화 팁**: "여러 Source를 한꺼번에 바꿀 때
|
### 핵심 아이디어 — 콜스택/코루틴이 아니라 값으로 지연 구간을 표현
|
||||||
store-bind가 걸린 파생값의 재계산/재대입이 중복되지 않도록 파이프라인
|
|
||||||
구성과 업데이트 순서에 유의하라"를 실용 가이드로 문서화. 예: 정말
|
Batch가 실패하는 근본 이유는 "지연 구간"을 콜스택/코루틴 스코프로
|
||||||
여러 값이 자주 같이 바뀐다면 애초에 하나의 State로 묶어서 `:Set()`을
|
표현하려 했기 때문이다. `Blocker`는 그 구간을 **그냥 사용자가 들고 있는
|
||||||
한 번만 하는 설계를 권장.
|
값**으로 표현한다 — `On()`/`Off()`를 부르는 두 시점 사이에 얼마나 많은
|
||||||
2. **quadnomicon(프레임워크 설계자 대상) 에세이**: "왜 Batch/Transaction이
|
yield/코루틴 전환이 끼어도, 심지어 완전히 다른 코루틴에서 `Off()`를
|
||||||
없는가" — 위 코루틴 yield 위험 분석을 근거로, Solid/MobX식 lexical
|
불러도 아무 문제가 없다. Batch를 무너뜨렸던 세 가지 실패 모드(전역 플래그
|
||||||
transaction이 협조적 스케줄링(코루틴) 환경에서 왜 근본적으로 위험한지
|
오염, 새 코루틴 미상속, 영구 yield로 인한 무기한 방치)가 구조적으로 전부
|
||||||
설명. `documentation-content-map.md`에 후보로 등록함(아래 "문서화
|
해당 안 됨.
|
||||||
백로그" 참고).
|
|
||||||
|
### 메커니즘 (확정)
|
||||||
|
|
||||||
|
```
|
||||||
|
Blocker() -> blocker -- 생성자
|
||||||
|
blocker:On() -> self -- IsBlocked = true로만 설정, 그 외 아무것도 안 함
|
||||||
|
blocker:Off() -> self -- IsBlocked = false로 먼저 설정, 그 다음 등록된
|
||||||
|
-- onunblock 핸들 전부 실행(순서 무관, idempotent)
|
||||||
|
|
||||||
|
state:Block(blocker) -> state -- 새 gated state 반환. **호출되는 즉시**(나중에
|
||||||
|
-- 처음 블록될 때가 아니라) onunblock 핸들을
|
||||||
|
-- blocker의 weak 배열에 등록.
|
||||||
|
```
|
||||||
|
|
||||||
|
gated state의 동작:
|
||||||
|
- 원본 state가 emit(무효화)될 때, 이 gated state로 전파를 시도.
|
||||||
|
- `blocker.IsBlocked`이면: 전파 안 하고 `HasBlockedEmit = true`만 세팅.
|
||||||
|
- `blocker.IsBlocked`가 아니면: 평소처럼 그냥 전파(투명하게 통과).
|
||||||
|
- `blocker:Off()`가 실행하는 onunblock 핸들은: `HasBlockedEmit`을 확인해
|
||||||
|
true면 그제서야 정확히 1회 전파(emit)하고 플래그를 리셋. 이미 false면
|
||||||
|
아무 것도 안 함(이미 언블록 상태에서 다시 Off를 불러도 안전 —
|
||||||
|
idempotent).
|
||||||
|
|
||||||
|
**`:Get()`엔 영향 없음** — 블록은 emit **전파**(eager 소비자에게 "바뀌었다"
|
||||||
|
알리는 신호)만 지연시킨다. 블록 중이라도 누군가 명시적으로 `:Get()`하면
|
||||||
|
그 순간의 실제 값을 정상적으로 계산해서 준다 — `store-semantics.md`의
|
||||||
|
"Get()은 라이브 레퍼런스를 준다" 원칙과 일치.
|
||||||
|
|
||||||
|
### 사용 예시 — `state1, state2 -> state3` 케이스의 정답
|
||||||
|
|
||||||
|
처음 문제 제기("state1, state2 -> state3로 갈 때 둘 다 업데이트하면 state3가
|
||||||
|
두 번 계산됨, 한번에 할 방법이 없다")에 대한 답: **`state3`(결합된 결과)
|
||||||
|
하나에만 `:Block`을 걸면 된다** — `state1`/`state2` 각각에 걸 필요 없음:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local blocker = Blocker()
|
||||||
|
local gated3 = state3:Block(blocker) -- 소비자는 gated3를 구독
|
||||||
|
|
||||||
|
blocker:On()
|
||||||
|
state1:Set(1) -- state3 무효화 → gated3로 전파 시도 → 블록됨 → HasBlockedEmit=true
|
||||||
|
state2:Set(2) -- state3 무효화 → gated3로 전파 시도 → 이미 true, 그대로
|
||||||
|
blocker:Off() -- onunblock 핸들 실행 → HasBlockedEmit 확인 → 딱 한 번 emit
|
||||||
|
```
|
||||||
|
|
||||||
|
**일반 사용 가이드(확정, 문서화 필수)**: Block은 **파이프라인의 최종
|
||||||
|
연산 지점**(실제로 무거운 계산이 일어나는 derived state, eager 소비자에
|
||||||
|
가장 가까운 지점)에 거는 게 원칙 — 소스가 여러 개든, 하나가 한 주기에
|
||||||
|
여러 번 바뀌든 상관없이 이 지점 하나만 지키면 됨. 소스 쪽에 각각 거는 게
|
||||||
|
아니다.
|
||||||
|
|
||||||
|
### 이름 확정
|
||||||
|
|
||||||
|
- 클래스: `Blocker` — `Observer`/`Modifier`/`Ref`와 같은 명사-행위자
|
||||||
|
네이밍 관례와 일치.
|
||||||
|
- Blocker 자신의 토글: **`On()`/`Off()` -> self** (`Block()`/`Unblock()`
|
||||||
|
아님) — `state:Block(blocker)`가 이미 "배선(wiring)" 동작의 동사로
|
||||||
|
"Block"을 쓰고 있어서, Blocker 자신의 토글까지 같은 단어를 쓰면
|
||||||
|
`blocker:Block()`(블로커를 켠다)과 `state:Block(blocker)`(state를 이
|
||||||
|
블로커에 배선한다)가 같은 단어로 다른 두 동작을 가리키게 됨 — 자체
|
||||||
|
API 안에서 `register`→`State` 리네임 때 겪었던 "모호함은 풀었는데
|
||||||
|
충돌이 새로 생긴" 패턴이 반복될 뻔한 걸 미리 피함.
|
||||||
|
- 필드: **`IsBlocked`**(Blocker 자신의 On/Off 상태) — `Enabled`보다
|
||||||
|
명확(enabled는 "정상 작동 중"으로도 읽혀 헷갈릴 수 있음).
|
||||||
|
- 필드: **`HasBlockedEmit`**(gated state의 대기 플래그) — `Is`/`Has`
|
||||||
|
접두어로 불리언임을 바로 알려줌.
|
||||||
|
- 메소드: `state:Block(blocker) -> state`.
|
||||||
|
|
||||||
|
### 재진입(네스팅) — 의도적으로 미지원, 강한 문서화 필수
|
||||||
|
|
||||||
|
`IsBlocked`는 카운터가 아니라 단순 불리언이고, **의도적으로 그렇게
|
||||||
|
둔다.** 레퍼런스 카운팅으로 네스팅을 지원할 수도 있었지만, 그러면
|
||||||
|
"`On()` 여러 번, `Off()` 실수로 적게" 같은 버그가 **영구 블록으로 조용히
|
||||||
|
새는** 더 위험한 실패 모드를 만든다. 언어 차원에서 "On~Off 사이 코드가
|
||||||
|
죽었는지, 스레드가 죽었는지"를 추적하는 것도 해키해서 하지 않기로 함(Rust의
|
||||||
|
"poisoned mutex" — 락 구간 안에서 패닉이 나면 락이 오염 상태가 되는 것과
|
||||||
|
유사한 문제의식, 그 트래킹 자체를 만들지 않기로 함).
|
||||||
|
|
||||||
|
**대신 확정된 규칙**: 겹치는 배치가 필요하면 **각자 새 `Blocker` 인스턴스를
|
||||||
|
만들 것** — 하나의 Blocker를 여러 컨텍스트에서 재사용/중첩하지 않는다.
|
||||||
|
`Off()`는 스태킹 없이 즉시 그 자리에서 꺼진다. **이 제약은 반드시 사용자
|
||||||
|
문서(API 레퍼런스 수준)에 명시적으로 강조할 것** — 네스팅을 시도하면
|
||||||
|
조용히 잘못된 시점에 조기 해제되는, 원인 추적이 어려운 버그로 이어짐.
|
||||||
|
|
||||||
|
### 상태: 핵심 메커니즘+이름 확정. 남은 건 문서화뿐
|
||||||
|
|
||||||
|
`Extract`/`Add`처럼 세부 시그니처가 더 필요한 다른 항목들과 달리, Blocker는
|
||||||
|
설계 질문이 남아있지 않음 — `base/`로 승격 가능한 수준. quadnomicon에서
|
||||||
|
"Batch를 기각하고 왜 Blocker로 갔는가"를 비교 설명하는 게 좋은 소재(아래
|
||||||
|
"문서화 백로그" 참고).
|
||||||
|
|
||||||
## 4. Context — 기각 확정, 레이어드 Store 대안도 철회 (결정 완료)
|
## 4. Context — 기각 확정, 레이어드 Store 대안도 철회 (결정 완료)
|
||||||
|
|
||||||
|
|
@ -312,24 +408,39 @@ Context, 레이어드 Store 둘 다 프리미티브로 만들지 않음. "왜 Co
|
||||||
- **디바운스/스로틀**: Fusion/Vide/v1 어디에도 공개 프리미티브로 없음 —
|
- **디바운스/스로틀**: Fusion/Vide/v1 어디에도 공개 프리미티브로 없음 —
|
||||||
세 레포 모두 없다는 것 자체가 "quad도 굳이 안 만들어도 된다"는 정황.
|
세 레포 모두 없다는 것 자체가 "quad도 굳이 안 만들어도 된다"는 정황.
|
||||||
|
|
||||||
## 문서화 백로그 (2026-08-06, `documentation-content-map.md`에도 반영)
|
## 문서화 백로그 (2026-08-06~07, `documentation-content-map.md`에도 반영)
|
||||||
|
|
||||||
- **quadnomicon 에세이**: "왜 Batch/Transaction이 없는가"(코루틴 yield
|
- **quadnomicon 에세이**:
|
||||||
위험), "왜 Context가 없는가"(명시적 타입 강제 Store 전달이 이미 그
|
- "왜 lexical Batch를 기각하고 대신 값 기반 Blocker를 택했는가" — 코루틴
|
||||||
역할을 함).
|
yield 위험 분석(Batch 절)과 Blocker의 설계(3-1절)를 나란히 비교.
|
||||||
- **심화 문서**: "여러 Source를 한꺼번에 바꿀 때 중복 재계산/재대입을
|
- "왜 Context가 없는가"(명시적 타입 강제 Store 전달이 이미 그 역할을 함).
|
||||||
피하는 파이프라인/업데이트 순서 최적화 팁"(Batch 없이 사용자가 직접
|
- "왜 push-invalidate/pull-recompute 설계가 laziness와 재계산 방지를
|
||||||
할 수 있는 것).
|
최우선 목표로 뒀는가" — Blocker 같은 파생 프리미티브가 이 목표 위에서
|
||||||
|
자연스럽게 나온 이유까지 포함해 기존 심화 콘텐츠 후보 3번(`왜
|
||||||
|
push-invalidate/pull-recompute인가`)을 더 깊게 확장.
|
||||||
|
- **심화 문서**:
|
||||||
|
- "State 파생 체인 동작 원리" — emit이 아래로 전파되고, `Get()` 요청이
|
||||||
|
위로 거슬러 올라가 재계산된 뒤 다시 아래로 내려오는 흐름을 명확히
|
||||||
|
설명(Blocker/Effect 둘 다 이 흐름 위에서 동작하므로 선행 이해로 필요).
|
||||||
|
- "`:Compute` 함수 안에서 `if` 등으로 일부 의존값만 조건부로 사용하는
|
||||||
|
유연한 구조" — 명시적 의존성 선언(`:With`) 위에서도 실제 계산은
|
||||||
|
조건부로 일부만 쓸 수 있다는 팁.
|
||||||
|
- "Blocker 사용 가이드" — 파이프라인 최종 연산 지점에 배치, **네스팅
|
||||||
|
금지를 강하게 명시**(위 3-1절 "재진입" 참고, 문서화 시 최우선 강조
|
||||||
|
항목).
|
||||||
|
- "여러 Source를 한꺼번에 바꿀 때 Blocker 없이도 중복 재계산을 피하는
|
||||||
|
파이프라인/업데이트 순서 팁"(Blocker를 안 쓰는 단순 케이스용 보조 팁,
|
||||||
|
기존 "심화 최적화 팁" 항목을 Blocker 존재를 전제로 재조정).
|
||||||
|
|
||||||
## 제안 우선순위
|
## 제안 우선순위
|
||||||
|
|
||||||
1. **키 기반 컬렉션 재조정** — 여전히 최우선, 설계 진행 중. M0 이전 완전
|
1. **키 기반 컬렉션 재조정** — 여전히 최우선, 설계 진행 중. M0 이전 완전
|
||||||
확정이 목표.
|
확정이 목표.
|
||||||
2. **Effect** — 거의 수렴, 시그니처만 다듬으면 됨.
|
2. **Effect** — 거의 수렴, 시그니처만 다듬으면 됨.
|
||||||
3. **Batch** — 결정 완료(프리미티브 없음, 문서화로 대체), 더 이상 검토
|
3. **Blocker** — 핵심 메커니즘+이름 확정, `base/`로 승격 가능한 수준.
|
||||||
대상 아님.
|
문서화(특히 네스팅 금지 강조)만 남음.
|
||||||
4. **Context** — 결정 완료(기각, 레이어드 Store도 철회), 더 이상 검토
|
4. **Batch(lexical)/Context** — 둘 다 결정 완료(기각), 더 이상 검토 대상
|
||||||
대상 아님.
|
아님.
|
||||||
|
|
||||||
## 참고: 조사에 사용한 소스 근거
|
## 참고: 조사에 사용한 소스 근거
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,23 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부
|
||||||
12. 독립 프리미티브 vs 파생 데이터 — 생성자 모양을 결정하는 원칙 — `store-semantics.md`
|
12. 독립 프리미티브 vs 파생 데이터 — 생성자 모양을 결정하는 원칙 — `store-semantics.md`
|
||||||
14. 왜 컴포넌트는 전역 store를 직접 참조하면 안 되는가(이식성) — `purity-and-effects-plan.md`
|
14. 왜 컴포넌트는 전역 store를 직접 참조하면 안 되는가(이식성) — `purity-and-effects-plan.md`
|
||||||
15. 왜 Source가 State를 구조적으로 만족하는가(Svelte Writable/Readable과 같은 서브타입 모양, `RefSource`/`StoreSource` 중간안이 왜 기각됐는가) — `store-semantics.md`, `component-composition-plan.md`
|
15. 왜 Source가 State를 구조적으로 만족하는가(Svelte Writable/Readable과 같은 서브타입 모양, `RefSource`/`StoreSource` 중간안이 왜 기각됐는가) — `store-semantics.md`, `component-composition-plan.md`
|
||||||
16. 여러 Source를 한꺼번에 바꿀 때 중복 재계산/재대입을 피하는 파이프라인/업데이트 순서 최적화 팁(Batch 프리미티브 없이 사용자가 직접 할 수 있는 것) — `research/additional-primitives-plan.md` "문서화 백로그" 절(2026-08-06 신설)
|
16. **State 파생 체인 동작 원리** — emit이 아래로 전파되고, `Get()` 요청이
|
||||||
|
위로 거슬러 올라가 재계산된 뒤 다시 아래로 내려오는 흐름을 명확히
|
||||||
|
설명(Blocker/Effect 둘 다 이 흐름 위에서 동작하므로 선행 이해로 필요)
|
||||||
|
— `research/additional-primitives-plan.md` "문서화 백로그" 절
|
||||||
|
(2026-08-06~07 신설)
|
||||||
|
17. **`:Compute` 함수 안에서 `if` 등으로 일부 의존값만 조건부로 사용하는
|
||||||
|
유연한 구조** — 명시적 의존성 선언(`:With`) 위에서도 실제 계산은
|
||||||
|
조건부로 일부만 쓸 수 있다는 팁 — `research/additional-primitives-plan.md`
|
||||||
|
"문서화 백로그" 절
|
||||||
|
18. **Blocker 사용 가이드** — 파이프라인 최종 연산 지점(무거운 계산이
|
||||||
|
실제 일어나는 derived state)에 배치하는 게 원칙이라는 것, **네스팅
|
||||||
|
금지를 최우선으로 강조**(겹치는 배치는 각자 새 `Blocker`를 만들 것 —
|
||||||
|
안 지키면 조용히 잘못된 시점에 조기 해제되는 원인 추적 어려운 버그로
|
||||||
|
이어짐) — `research/additional-primitives-plan.md` 3-1절
|
||||||
|
19. 여러 Source를 한꺼번에 바꿀 때 Blocker 없이도 중복 재계산/재대입을
|
||||||
|
피하는 파이프라인/업데이트 순서 팁(Blocker를 안 쓰는 단순 케이스용
|
||||||
|
보조 팁) — `research/additional-primitives-plan.md` "문서화 백로그" 절
|
||||||
|
|
||||||
(13번이었던 "Fusion/Vide 경험자용 비교 섹션"은 2026-08-06 재분류로 아래 6번
|
(13번이었던 "Fusion/Vide 경험자용 비교 섹션"은 2026-08-06 재분류로 아래 6번
|
||||||
`quadnomicon`으로 이동)
|
`quadnomicon`으로 이동)
|
||||||
|
|
@ -142,19 +158,28 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부
|
||||||
2. `:With`+`:Compute` 명시적 파생값이 Vide의 암묵적 ambient stack 대신
|
2. `:With`+`:Compute` 명시적 파생값이 Vide의 암묵적 ambient stack 대신
|
||||||
채택된 이유 — Vide 경험자 대상 비교
|
채택된 이유 — Vide 경험자 대상 비교
|
||||||
|
|
||||||
**2026-08-06 후속 세션에서 추가된 후보(둘 다 `research/
|
**2026-08-06~07 후속 세션에서 추가된 후보(전부 `research/
|
||||||
additional-primitives-plan.md`의 "문서화 백로그" 절이 원자료)**:
|
additional-primitives-plan.md`의 "문서화 백로그" 절이 원자료)**:
|
||||||
3. 왜 Batch/Transaction이 없는가 — Solid `batch()`/MobX `runInAction()`류
|
3. **왜 lexical Batch를 기각하고 대신 값 기반 Blocker를 택했는가** —
|
||||||
lexical transaction이 Roblox의 협조적 스케줄링(코루틴 yield) 환경에서
|
Solid `batch()`/MobX `runInAction()`류 lexical transaction이 Roblox의
|
||||||
왜 근본적으로 위험한지(전역/코루틴 스코프 플래그가 새 코루틴 스폰·영구
|
협조적 스케줄링(코루틴 yield) 환경에서 왜 근본적으로 위험한지(전역/
|
||||||
yield에 어떻게 깨지는지 구체 시나리오 포함) — Fusion/Vide 비교는 아니고
|
코루틴 스코프 플래그가 새 코루틴 스폰·영구 yield에 어떻게 깨지는지
|
||||||
"설계 원리"형 에세이라 Rustonomicon 패러디 취지(비슷한 프레임워크
|
구체 시나리오) **+** 그 대안으로 `Blocker`(콜스택/코루틴이 아니라
|
||||||
설계자용)와 잘 맞음
|
값으로 지연 구간을 표현, 네스팅 의도적 미지원)가 어떻게 같은 문제를
|
||||||
|
구조적으로 우회하는지 나란히 비교 — Fusion/Vide 비교는 아니고 "설계
|
||||||
|
원리"형 에세이라 Rustonomicon 패러디 취지(비슷한 프레임워크 설계자용)와
|
||||||
|
잘 맞음. (2026-08-06 세션엔 "왜 Batch가 없는가"로만 다뤘다가, Blocker
|
||||||
|
채택 후 2026-08-07 세션에서 비교 에세이로 재구성됨 — Batch(lexical)
|
||||||
|
기각과 Blocker 채택은 별개 결정이니 혼동하지 말 것.)
|
||||||
4. 왜 Context가 없는가 — 얕은 버전(코루틴 키 weak table push-pop)조차
|
4. 왜 Context가 없는가 — 얕은 버전(코루틴 키 weak table push-pop)조차
|
||||||
quad가 정상 패턴으로 확정한 Slot 비동기 추가에서 조용히 깨지는 이유,
|
quad가 정상 패턴으로 확정한 Slot 비동기 추가에서 조용히 깨지는 이유,
|
||||||
완전 자동 버전이 Roblox Luau의 플랫폼 한계(thread-local 없음)로 불가한
|
완전 자동 버전이 Roblox Luau의 플랫폼 한계(thread-local 없음)로 불가한
|
||||||
이유, 명시적 타입 강제 Store 전달이 Context보다 안전한 이유(레이어드
|
이유, 명시적 타입 강제 Store 전달이 Context보다 안전한 이유(레이어드
|
||||||
Store 대안도 왜 함께 기각됐는지 포함)
|
Store 대안도 왜 함께 기각됐는지 포함)
|
||||||
|
5. 왜 push-invalidate/pull-recompute 설계가 laziness와 재계산 방지를
|
||||||
|
최우선 목표로 뒀는가 — 위 `심화` 3번(`왜 push-invalidate/pull-recompute
|
||||||
|
인가`)을 더 깊게 확장, `Blocker` 같은 파생 프리미티브가 이 목표 위에서
|
||||||
|
왜 자연스럽게 나왔는지까지 포함하는 설계 철학 에세이
|
||||||
|
|
||||||
**publish 안 하는 것과의 경계**: 세션별 정정 이력, 조사 원자료(Fusion
|
**publish 안 하는 것과의 경계**: 세션별 정정 이력, 조사 원자료(Fusion
|
||||||
반응 그래프 BFS 분석, quad2-try 죽은 코드 조사 등)는 quadnomicon에도
|
반응 그래프 BFS 분석, quad2-try 죽은 코드 조사 등)는 quadnomicon에도
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue