enum-driven-state-authoring
When writing new code, model state as sum types from the start. Apply when creating entities with distinct modes, phases, or lifecycle states.
安装与使用
请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。
查看来源 ↗使用指南技能说明
Enum-Driven State: Authoring Guide
When writing new code, model state as sum types from the start. Don't create optional fields that depend on other fields.
Before You Write
Ask these questions about your data:
- What are the mutually exclusive states? (e.g., Loading, Success, Error)
-
For each state, what data is required? (e.g., Success needs
data, Error needserror) -
Is there any data shared across ALL states? (e.g.,
id,timestamp→ keep on wrapper)
Decision Flow
Does this entity have distinct modes/phases/states?
│
├─ YES → Define a sum type with one variant per state
│ Each variant holds only its required data
│
└─ NO → A simple struct/class is fine
But watch for optional fields creeping in later
Patterns to Apply
Async/Remote Data
// Start here, not with optional fields
type RemoteData<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
Polymorphic Entities
// When type determines structure, use enum not optionals
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
Workflow/Lifecycle States
@dataclass
class Draft:
content: str
@dataclass
class Published:
content: str
published_at: datetime
url: str
@dataclass
class Archived:
content: str
archived_at: datetime
reason: str
Article = Draft | Published | Archived
Connection/Session State
sealed interface ConnectionState {
data object Disconnected : ConnectionState
data class Connecting(val attempt: Int) : ConnectionState
data class Connected(val socket: Socket) : ConnectionState
data class Error(val reason: Throwable) : ConnectionState
}
Anti-Patterns to Avoid
❌ Don't start with this:
interface User {
id: string;
status: 'guest' | 'registered' | 'premium';
email?: string; // only if registered/premium
subscriptionId?: string; // only if premium
expiresAt?: Date; // only if premium
}
✅ Start with this:
type User =
| { status: 'guest'; id: string }
| { status: 'registered'; id: string; email: string }
| { status: 'premium'; id: string; email: string; subscriptionId: string; expiresAt: Date };
Shared Data Pattern
When some fields exist on ALL variants, wrap them:
struct Event {
id: Uuid,
timestamp: DateTime,
payload: EventPayload, // The sum type
}
enum EventPayload {
UserCreated { name: String },
OrderPlaced { items: Vec<Item> },
PaymentFailed { reason: String },
}
Checklist When Authoring
- Identified all mutually exclusive states
- Each state has a dedicated variant/case
- Data lives INSIDE its variant, not as optional sibling
- Shared fields are on a wrapper struct
- No optional field depends on another field's value
使用示例
查看来源仓库中的使用示例与配置说明。
查看来源 ↗来源历史
- 收录时间
- 2026-01-20
- 索引更新时间
- 2026-01-26
- Branch
- main
收录与同步日期属于本站记录。源码修改历史请查看来源仓库。
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!