fix(slot): Splice가 vararg인 진짜 이유는 T가 opaque 제네릭이라서
직전 커밋이 "Slot의 T(Instance|Slot)가 우연히 테이블이라 T|{T}가
모호하다"고 적었는데, 이는 quad-roblox라는 특정 백엔드의 구체적 T에
기댄 특수 사례일 뿐 근본 원인이 아님 — 정확한 이유는 Slot<T>가 base
레벨에선 T가 뭔지 전혀 모르는 제네릭(다른 백엔드면 테이블/userdata/
그 밖에 뭐든 가능)이라, 바깥 {}가 단일 T인지 {T} 배열인지 원천적으로
판별 불가능하다는 것.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
35306ed5ca
commit
b91c662f50
3 changed files with 26 additions and 13 deletions
|
|
@ -376,13 +376,19 @@ Remove/Extract/Move하려 해도 참조를 안 들고 있는 경우가 잦음. `
|
|||
`Slot:Add`가 이미 받는 `State<T>`/nested `Slot` 요소로 흡수 가능
|
||||
(Slot-in-Slot, 위 "반응형 raw 요소"/"Slot-in-Slot 중첩" 절) — `Splice`
|
||||
자체가 동적 배치를 떠받칠 이유가 없음. (2) **`T | {T}`가 여기선 오히려
|
||||
틀림** — `Tag`의 `T`는 항상 plain `string`이라 `type(v) == "table"`
|
||||
분기가 "배열이냐 아니냐"를 안전하게 구분하지만, `Slot`의 `T`(`Instance
|
||||
| Slot<Instance>`)는 `Slot` 자신이 이미 테이블이라 "단일 `T`(마침
|
||||
테이블인)"와 "`{T}` 배열"을 구분할 방법이 없음 — 억지로 하려면
|
||||
결국 항상 명시적으로 감싸거나 풀어야 해서 vararg 대비 얻는 게 없음.
|
||||
이미 `{T}` 배열을 들고 있는 호출부는 `Splice(i, n, table.unpack(list))`로
|
||||
충분(리스트가 하나뿐이라 tail-position 제약에도 안 걸림).
|
||||
틀림 — 이유는 "`Slot`의 `T`가 우연히 테이블(`Instance | Slot<Instance>`)
|
||||
이라서"가 아니라, `Slot<T>`가 base 레벨에선 `T`가 뭔지 전혀 모르는
|
||||
제네릭이기 때문(다른 백엔드면 테이블일 수도, userdata일 수도, 그 밖에
|
||||
뭐든 될 수 있음)** — `Tag`는 `T=string`으로 항상 고정·확정돼 있어
|
||||
`type(v) == "table"` 분기가 "배열이냐 아니냐"를 안전하게 구분하지만,
|
||||
`Splice(idx, len, {item1, item2})`의 `{}`가 "그 자체로 하나의 `T`
|
||||
값(마침 테이블로 표현된)"인지 "펼쳐야 할 `{T}` 배열"인지는 `T`가 뭔지
|
||||
base가 애초에 모르므로 원천적으로 판별 불가능(quad-roblox에서 `T`에
|
||||
`Slot`이 섞여있는 건 이 문제를 드러내는 한 사례일 뿐, 근본 원인이
|
||||
아님). 억지로 하려면 결국 항상 명시적으로 감싸거나 풀어야 해서 vararg
|
||||
대비 얻는 게 없음. 이미 `{T}` 배열을 들고 있는 호출부는
|
||||
`Splice(i, n, table.unpack(list))`로 충분(리스트가 하나뿐이라
|
||||
tail-position 제약에도 안 걸림).
|
||||
- **`Get`/`IndexOf` 신설, 원래 "YAGNI"로 뺐던 것을 재추가.** 처음엔
|
||||
"`:List`가 자기 key→element 맵을 따로 들고 있어 Slot 내부 상태 조회가
|
||||
불필요"하다고 판단해 드롭했으나, 위 인덱스 기준 전환과 맞물려 다시
|
||||
|
|
|
|||
|
|
@ -54,7 +54,13 @@
|
|||
`newElements`에도 적용되는지 검토했으나 **기각, vararg 유지** — (1)
|
||||
한 번에 갈아끼우는 요소 수는 호출부가 아는 소수인 경우가 대부분이고
|
||||
정말 동적이면 Slot-in-Slot으로 흡수 가능해 `Splice` 자체가 그 케이스를
|
||||
떠받칠 이유가 없음, (2) `T | {T}`는 `Slot`의 `T`(`Instance | Slot`)가
|
||||
이미 테이블이라 "단일 T(마침 테이블)"와 "{T} 배열"을 구분 못 해 오히려
|
||||
틀림 — `Tag`의 `T=string`(항상 non-table)이라 안전했던 것과 다름
|
||||
(`slot-plan.md`에 반영).
|
||||
떠받칠 이유가 없음, (2) `T | {T}`는 오히려 틀림 — **처음엔 이 이유를
|
||||
"`Slot`의 `T`(`Instance | Slot`)가 이미 테이블이라서"로 적었으나, 사용자가
|
||||
재지적: 진짜 이유는 `T`가 우연히 테이블을 포함해서가 아니라 `Slot<T>`가
|
||||
base 레벨에선 `T`가 뭔지 전혀 모르는 제네릭이기 때문(다른 백엔드면
|
||||
테이블/userdata/그 밖에 뭐든 가능)** — 바깥 `{}`가 "그 자체로 하나의
|
||||
`T`(마침 테이블로 표현된)"인지 "펼쳐야 할 `{T}` 배열"인지 base가 `T`의
|
||||
정체를 모르므로 원천적으로 판별 불가능, quad-roblox의 `T`에 `Slot`이
|
||||
섞여있는 건 이 문제를 드러내는 한 사례일 뿐 근본 원인이 아님 — `Tag`의
|
||||
`T=string`(항상 고정·확정)이라 안전했던 것과 다름(`slot-plan.md`에 이
|
||||
정정으로 반영).
|
||||
|
|
|
|||
|
|
@ -642,5 +642,6 @@ vararg, `Slot:Splice` 신설** (`session/2026-08-12-15-slot-in-slot-relate-scope
|
|||
removeCount, ...newElements)` CRUD 신설 — 구간 제거+삽입을 shift/recompute
|
||||
1회로 묶는 순수 최적화. `newElements`는 `Tag:Added`와 달리 의도적으로
|
||||
vararg 유지(요소 개수가 대개 소수로 고정, 동적이면 Slot-in-Slot으로 흡수
|
||||
가능, `T|{T}`는 `Slot`의 `T` 자체가 테이블이라 오히려 모호해짐,
|
||||
`slot-plan.md`).
|
||||
가능, `T|{T}`는 `Slot<T>`가 base 레벨에선 `T`가 뭔지 모르는 제네릭이라
|
||||
바깥 `{}`가 단일 T인지 배열인지 원천적으로 판별 불가능해서 오히려
|
||||
모호해짐 — `Slot`의 T에 우연히 Slot이 섞여서가 아님, `slot-plan.md`).
|
||||
|
|
|
|||
Loading…
Reference in a new issue