--!strict export type StateData = { Get: (self: StateData) -> T } export type State = StateData & { Compute: (self: StateData, fn: (self: StateData) -> U) -> State, Apply: (self: State, factory: (State) -> U) -> U, Gate: (self: State, setup: (emit: () -> ()) -> (() -> ())) -> State, Block: (self: State, blocker: Blocker) -> State, } export type SourceData = { Get: (self: SourceData) -> T, Revision: number } export type Source = SourceData & { Set: (self: SourceData, v: T) -> SourceData, Apply: (self: Source, factory: (State) -> U) -> U, Gate: (self: Source, setup: (emit: () -> ()) -> (() -> ())) -> State, } export type Blocker = { On: (self: Blocker) -> Blocker, Off: (self: Blocker) -> Blocker, OffWithoutEmit: (self: Blocker) -> Blocker, IsOn: (self: Blocker) -> boolean, IsBlocked: boolean, Policy: (self: Blocker, emit: () -> ()) -> (() -> ()), } local st: State = nil :: any local src: Source = nil :: any local b: Blocker = nil :: any -- (A) gate-plan.md 5번의 확정 형태 local g1 = st:Gate(function(emit) return b:Policy(emit) end) -- (B) tween-plan.md의 관용구 — Source에 State용 팩토리를 붙인다 local Animate: (self: State) -> State = nil :: any local a1 = src:Apply(Animate) local a2 = st:Apply(Animate) -- (C) Debounce 정책 모양 — 자기 Blocker를 사적으로 하나 갖는 setup local d = st:Gate(function(emit) local mine: Blocker = nil :: any local pass = mine:Policy(emit) return function() pass() end end) -- (D) 음성 대조군 — gate-plan.md가 "문법 오류"라고 정정한 형태 local bad = st:Gate(b.Policy)