From 4dd7659620bfa5b0603a97d44faa0eb88e5c9637 Mon Sep 17 00:00:00 2001 From: qwreey Date: Fri, 7 Aug 2026 16:01:05 +0900 Subject: [PATCH] =?UTF-8?q?docs(base):=20Override=20=EC=84=9C=EB=B8=8C?= =?UTF-8?q?=ED=83=80=EC=9E=85=20Modifier=20=ED=83=80=EC=9E=85=20=EC=8B=9C?= =?UTF-8?q?=EA=B7=B8=EB=8B=88=EC=B2=98=20=EB=AF=B8=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FrameModifier/GuiObjectModifier처럼 서브타입 관계인 Modifier끼리 Override로 섞을 때 필드 setter 리턴 타입이 갈려 구조적 서브타이핑이 안 풀릴 수 있음을 modifier-plan.md 9-2번에 남기고, 실 Luau 테스트가 필요한 항목으로 ROADMAP.md M7에 추가. Co-Authored-By: Claude Sonnet 5 --- .claude/base/modifier-plan.md | 30 ++++++++++++++++++++++++++++++ ROADMAP.md | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/.claude/base/modifier-plan.md b/.claude/base/modifier-plan.md index 0e5894e..ae9eb76 100644 --- a/.claude/base/modifier-plan.md +++ b/.claude/base/modifier-plan.md @@ -389,6 +389,36 @@ Modifier 값을 변수/모듈 상수로 만들어 재사용하는, 기존에도 근거 없는 선제 최적화라 설계하지 않음 — CLAUDE.md의 "드문 오용/가상 미래 요구까지 방어/최적화하려고 구조를 복잡하게 만들지 않는다" 원칙과 동일. +### 9-2. `Override`가 서로 다른(그러나 상하위 관계인) Modifier 타입을 섞는 경우 — +타입 시그니처 미확정, 실 Luau 테스트 필요 (2026-08-07 다섯 번째 세션 후속) + +**문제**: Modifier 타입은 위 4번 절 "FrameModifier 타입" 언급대로 Roblox +클래스별로 생성기가 뽑아내는 flat 타입인데, 그 밑의 Roblox 클래스 자체엔 +서브타입 관계가 있음(`Frame`이 `GuiObject`의 서브클래스) — 그럼 생성된 +`FrameModifier`도 `GuiObjectModifier`의 서브타입이어야 자연스럽고, 실제로 +`Modifier.Override(guiObjectMod, frameMod)`처럼 공통 상위 클래스 스타일 +프리셋과 하위 클래스 전용 보정을 섞어 합치는 패턴이 필요해 보임(사용자 +지적, 2026-08-07). + +**막히는 지점**: 필드 setter 메소드(`:FontSize` 류)는 각 타입마다 반환 +타입이 자기 자신(`self`, 즉 `FrameModifier`는 `FrameModifier`를, +`GuiObjectModifier`는 `GuiObjectModifier`를 리턴)이라, 같은 이름의 메소드 +필드끼리 리턴 타입이 갈려서 단순 구조적 서브타이핑만으로는 안 풀릴 가능성이 +있음. + +**후보안(미검증)**: 같은 이름의 메소드 필드는 리턴 타입이 다르니 그냥 +`any`로 뭉개고, 나머지(메소드가 아닌 순수 데이터 필드) 쪽만 `[string]: nil`류 +인덱스 시그니처 조건이 성립하면 통과시키는 식으로 서브타입 호환을 흉내낼 수 +있는지 — 이게 실제로 Luau 솔버에서 받아들여지는 타입 구성인지는 추론만으로 +결론 낼 수 없고 실제 코드로 테스트해봐야 함(M0가 이미 검증 대상으로 삼은 +"추론만으로 확정하고 실제 Luau로 부딪혀본 적 없는 것"과 같은 성격의 모호함). + +**당장의 fallback**: 위 후보안이 Luau에서 실제로 안 먹히는 걸로 확인되면, +`Modifier.Override`의 타입 시그니처를 일단 `Override(...: any): any`류로 +느슨하게 열어 정적 체크를 포기 — 이건 임시 처치로 명시하고, M7 실제 구현 +시점에 실 테스트 결과에 따라 다시 좁히는 걸 목표로 로드맵에 남김 +(`ROADMAP.md` M7). + **`:Peek<>(key): T | State | nil`** — Modifier 필드를 확정하지 않고 그대로 읽는 접근자. 이름을 `Get`이 아니라 `Peek`로 정한 이유: 이 프로젝트 전역에서 `State:Get()`은 "확정한다"(pull + recompute + 최종값 diff --git a/ROADMAP.md b/ROADMAP.md index dfedd72..b267f66 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -94,6 +94,10 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검 - [ ] flatten-before-dispatch, immutable `table.clone` 체이닝 - [ ] `Modifier.Override(mod1, mod2, ...)`(가칭, 구 `Merge`) — 필드별 raw 덮어쓰기, 특별한 State/함수 분기 불필요(`modifier-plan.md` 9번) +- [ ] `Override`가 서브타입 관계인 서로 다른 Modifier 타입(예: `FrameModifier`/ + `GuiObjectModifier`)을 섞을 때의 타입 시그니처 실 Luau 테스트 + (`modifier-plan.md` 9-2번, 미검증 — 안 되면 일단 `Override(...: any): + any`로 느슨하게 열어두고 이 항목으로 되돌아올 것) - [ ] `State` 조합 타입 차단 확인(`modifier-plan.md` 7번, UB 확정) - [ ] `:Apply(factory)` 팩토리 함수 체이닝(`modifier-plan.md` 8번, 예약 키 `Apply`가 제네릭 `__index` 필드 setter와 안 겹치는지 확인)