@martinffx/atelier 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +285 -0
  4. package/agents/architect.md +38 -0
  5. package/agents/oracle.md +40 -0
  6. package/agents/recon.md +39 -0
  7. package/dist/atelier.js +33082 -0
  8. package/package.json +44 -0
  9. package/skills/code-conventional-commit/SKILL.md +134 -0
  10. package/skills/code-conventional-commit/evals/evals.json +23 -0
  11. package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/commit-message.txt +4 -0
  12. package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/eval_metadata.json +6 -0
  13. package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/eval_metadata.json +6 -0
  14. package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/validation-result.txt +16 -0
  15. package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/commit-message.txt +8 -0
  16. package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/eval_metadata.json +6 -0
  17. package/skills/code-conventional-commit/workspace/iteration-1/report.html +1325 -0
  18. package/skills/code-debug/SKILL.md +271 -0
  19. package/skills/code-debug/references/language-tools.md +139 -0
  20. package/skills/code-debug/references/techniques.md +117 -0
  21. package/skills/code-docs/SKILL.md +171 -0
  22. package/skills/code-docs/references/api-docs.md +116 -0
  23. package/skills/code-docs/references/changelog.md +64 -0
  24. package/skills/code-docs/references/readme-structure.md +94 -0
  25. package/skills/code-git-worktrees/SKILL.md +218 -0
  26. package/skills/code-perf/SKILL.md +163 -0
  27. package/skills/code-perf/references/common-bottlenecks.md +124 -0
  28. package/skills/code-perf/references/optimization-patterns.md +167 -0
  29. package/skills/code-perf/references/profiling-tools.md +126 -0
  30. package/skills/code-review/SKILL.md +78 -0
  31. package/skills/code-review/references/output.md +200 -0
  32. package/skills/code-review/references/reviewers.md +719 -0
  33. package/skills/code-review/references/rq.md +357 -0
  34. package/skills/code-review/references/rs.md +30 -0
  35. package/skills/code-security/SKILL.md +183 -0
  36. package/skills/code-security/references/owasp-top-10.md +129 -0
  37. package/skills/code-security/references/security-tools.md +102 -0
  38. package/skills/code-security/references/vulnerability-patterns.md +129 -0
  39. package/skills/code-stacked-commit/SKILL.md +155 -0
  40. package/skills/code-subagents/SKILL.md +184 -0
  41. package/skills/code-subagents/references/code-quality-reviewer-prompt.md +48 -0
  42. package/skills/code-subagents/references/implementor-prompt.md +79 -0
  43. package/skills/code-subagents/references/spec-reviewer-prompt.md +62 -0
  44. package/skills/oracle-architect/SKILL.md +246 -0
  45. package/skills/oracle-architect/references/api-design.md +345 -0
  46. package/skills/oracle-architect/references/data-modeling.md +318 -0
  47. package/skills/oracle-architect/references/ddd-patterns.md +247 -0
  48. package/skills/oracle-challenge/SKILL.md +126 -0
  49. package/skills/oracle-testing/SKILL.md +237 -0
  50. package/skills/oracle-testing/references/anti-patterns.md +618 -0
  51. package/skills/oracle-testing/references/boundaries.md +579 -0
  52. package/skills/oracle-testing/references/core-testing.md +691 -0
  53. package/skills/oracle-testing/references/edge-testing.md +794 -0
  54. package/skills/oracle-testing/references/stub-driven-tdd.md +322 -0
  55. package/skills/oracle-thinkdeep/SKILL.md +164 -0
  56. package/skills/python-architecture/SKILL.md +148 -0
  57. package/skills/python-architecture/references/data-modeling.md +558 -0
  58. package/skills/python-architecture/references/ddd.md +569 -0
  59. package/skills/python-architecture/references/functional-core.md +337 -0
  60. package/skills/python-build-tools/SKILL.md +153 -0
  61. package/skills/python-build-tools/references/basedpyright.md +321 -0
  62. package/skills/python-build-tools/references/ruff.md +225 -0
  63. package/skills/python-build-tools/references/uv.md +257 -0
  64. package/skills/python-fastapi/SKILL.md +184 -0
  65. package/skills/python-fastapi/references/api-design.md +563 -0
  66. package/skills/python-fastapi/references/dependencies.md +479 -0
  67. package/skills/python-fastapi/references/middleware.md +475 -0
  68. package/skills/python-fastapi/references/validation.md +497 -0
  69. package/skills/python-modern-python/SKILL.md +201 -0
  70. package/skills/python-modern-python/references/async.md +76 -0
  71. package/skills/python-modern-python/references/pattern-matching.md +89 -0
  72. package/skills/python-modern-python/references/typing.md +96 -0
  73. package/skills/python-monorepo/SKILL.md +146 -0
  74. package/skills/python-monorepo/references/docker.md +515 -0
  75. package/skills/python-monorepo/references/namespace-packages.md +426 -0
  76. package/skills/python-monorepo/references/workspace-config.md +341 -0
  77. package/skills/python-sqlalchemy/SKILL.md +119 -0
  78. package/skills/python-sqlalchemy/references/async.md +51 -0
  79. package/skills/python-sqlalchemy/references/models.md +67 -0
  80. package/skills/python-sqlalchemy/references/queries.md +57 -0
  81. package/skills/python-temporal/SKILL.md +128 -0
  82. package/skills/python-temporal/references/patterns.md +87 -0
  83. package/skills/python-temporal/references/testing.md +54 -0
  84. package/skills/python-testing/SKILL.md +161 -0
  85. package/skills/python-testing/references/boundaries.md +449 -0
  86. package/skills/python-testing/references/mocking.md +449 -0
  87. package/skills/python-testing/references/pytest.md +524 -0
  88. package/skills/spec-brainstorm/SKILL.md +325 -0
  89. package/skills/spec-finish/SKILL.md +159 -0
  90. package/skills/spec-implement/SKILL.md +296 -0
  91. package/skills/spec-orchestrator/SKILL.md +156 -0
  92. package/skills/spec-plan/SKILL.md +248 -0
  93. package/skills/typescript-api-design/SKILL.md +396 -0
  94. package/skills/typescript-api-design/references/error-responses.md +469 -0
  95. package/skills/typescript-build-tools/SKILL.md +447 -0
  96. package/skills/typescript-build-tools/references/biome.md +436 -0
  97. package/skills/typescript-build-tools/references/bun.md +432 -0
  98. package/skills/typescript-build-tools/references/turborepo.md +426 -0
  99. package/skills/typescript-build-tools/references/vitest.md +518 -0
  100. package/skills/typescript-drizzle-orm/SKILL.md +398 -0
  101. package/skills/typescript-drizzle-orm/references/cloudflare.md +392 -0
  102. package/skills/typescript-drizzle-orm/references/entity-pattern.md +412 -0
  103. package/skills/typescript-drizzle-orm/references/postgresql.md +191 -0
  104. package/skills/typescript-drizzle-orm/references/repository-pattern.md +479 -0
  105. package/skills/typescript-drizzle-orm/references/sqlite.md +291 -0
  106. package/skills/typescript-dynamodb-toolbox/SKILL.md +370 -0
  107. package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +574 -0
  108. package/skills/typescript-dynamodb-toolbox/references/error-handling.md +495 -0
  109. package/skills/typescript-dynamodb-toolbox/references/modeling.md +332 -0
  110. package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +631 -0
  111. package/skills/typescript-dynamodb-toolbox/references/testing.md +718 -0
  112. package/skills/typescript-dynamodb-toolbox/references/transactions.md +423 -0
  113. package/skills/typescript-effect-ts/SKILL.md +341 -0
  114. package/skills/typescript-effect-ts/references/core.md +412 -0
  115. package/skills/typescript-effect-ts/references/error-handling.md +468 -0
  116. package/skills/typescript-effect-ts/references/resources.md +476 -0
  117. package/skills/typescript-effect-ts/references/schema.md +536 -0
  118. package/skills/typescript-effect-ts/references/services.md +461 -0
  119. package/skills/typescript-fastify/SKILL.md +405 -0
  120. package/skills/typescript-fastify/references/plugins.md +329 -0
  121. package/skills/typescript-fastify/references/typeid.md +315 -0
  122. package/skills/typescript-functional-patterns/SKILL.md +402 -0
  123. package/skills/typescript-functional-patterns/references/adts.md +497 -0
  124. package/skills/typescript-functional-patterns/references/branded-types.md +542 -0
  125. package/skills/typescript-functional-patterns/references/migration-guide.md +695 -0
  126. package/skills/typescript-functional-patterns/references/option-result.md +585 -0
  127. package/skills/typescript-testing/SKILL.md +353 -0
  128. package/skills/typescript-testing/references/mocking.md +675 -0
  129. package/skills/typescript-testing/references/msw.md +852 -0
  130. package/skills/typescript-testing/references/snapshot-testing.md +774 -0
@@ -0,0 +1,585 @@
1
+ # Option and Result Types for Type-Safe Error Handling
2
+
3
+ Option and Result types make error handling and nullable values explicit in function signatures. They force callers to handle failure cases, eliminating null pointer errors and uncaught exceptions.
4
+
5
+ ## Option Type
6
+
7
+ Option represents "a value that may be absent." It's an explicit alternative to `null` or `undefined`.
8
+
9
+ ### Definition
10
+
11
+ ```typescript
12
+ type None = { _tag: "None" }
13
+ type Some<T> = { _tag: "Some"; value: T }
14
+ type Option<T> = None | Some<T>
15
+
16
+ const None: None = { _tag: "None" }
17
+ const Some = <T>(value: T): Option<T> => ({ _tag: "Some", value })
18
+ ```
19
+
20
+ ### Basic Usage
21
+
22
+ ```typescript
23
+ function findUser(id: string): Option<User> {
24
+ const user = database.get(id)
25
+ return user ? Some(user) : None
26
+ }
27
+
28
+ const result = findUser("123")
29
+ switch (result._tag) {
30
+ case "Some":
31
+ console.log(`Found user: ${result.value.name}`)
32
+ break
33
+ case "None":
34
+ console.log("User not found")
35
+ break
36
+ }
37
+ ```
38
+
39
+ ### When to Use Option
40
+
41
+ Use Option when:
42
+ - A value may legitimately be absent
43
+ - Absence is an expected, valid outcome (not an error)
44
+ - You want to chain operations that may not find values
45
+ - You want to make nullability explicit in APIs
46
+
47
+ **Example scenarios:**
48
+ - Finding a user by ID (may not exist)
49
+ - Getting a value from a map
50
+ - First/last element of an array
51
+ - Parsing optional configuration fields
52
+
53
+ ### Option Utilities
54
+
55
+ ```typescript
56
+ // Check variant
57
+ const isNone = <T>(opt: Option<T>): opt is None => opt._tag === "None"
58
+ const isSome = <T>(opt: Option<T>): opt is Some<T> => opt._tag === "Some"
59
+
60
+ // Get value or default
61
+ const getOrElse = <T>(opt: Option<T>, defaultValue: T): T =>
62
+ opt._tag === "Some" ? opt.value : defaultValue
63
+
64
+ // Transform value if present
65
+ const map = <T, U>(opt: Option<T>, fn: (value: T) => U): Option<U> =>
66
+ opt._tag === "Some" ? Some(fn(opt.value)) : None
67
+
68
+ // Chain optional operations
69
+ const flatMap = <T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Option<U> =>
70
+ opt._tag === "Some" ? fn(opt.value) : None
71
+
72
+ // Filter value based on predicate
73
+ const filter = <T>(opt: Option<T>, predicate: (value: T) => boolean): Option<T> =>
74
+ opt._tag === "Some" && predicate(opt.value) ? opt : None
75
+
76
+ // Convert to array (for iteration)
77
+ const toArray = <T>(opt: Option<T>): T[] =>
78
+ opt._tag === "Some" ? [opt.value] : []
79
+ ```
80
+
81
+ ### Option Examples
82
+
83
+ #### Configuration Parsing
84
+
85
+ ```typescript
86
+ type Config = {
87
+ port: number
88
+ host: string
89
+ logLevel: Option<string> // Optional field
90
+ maxConnections: Option<number>
91
+ }
92
+
93
+ function parseConfig(raw: unknown): Config {
94
+ const data = raw as any
95
+ return {
96
+ port: data.port,
97
+ host: data.host,
98
+ logLevel: data.logLevel ? Some(data.logLevel) : None,
99
+ maxConnections: data.maxConnections ? Some(data.maxConnections) : None,
100
+ }
101
+ }
102
+
103
+ const config = parseConfig({ port: 3000, host: "localhost" })
104
+
105
+ // Use getOrElse for defaults
106
+ const logLevel = getOrElse(config.logLevel, "info")
107
+ const maxConnections = getOrElse(config.maxConnections, 100)
108
+ ```
109
+
110
+ #### Chaining Operations
111
+
112
+ ```typescript
113
+ type User = { id: string; name: string; email: string }
114
+ type Profile = { bio: string; avatar: string }
115
+
116
+ function findUser(id: string): Option<User> {
117
+ // ...
118
+ }
119
+
120
+ function getProfile(user: User): Option<Profile> {
121
+ // ...
122
+ }
123
+
124
+ // Chain without nested if statements
125
+ const profile = flatMap(findUser("123"), getProfile)
126
+
127
+ switch (profile._tag) {
128
+ case "Some":
129
+ console.log(profile.value.bio)
130
+ break
131
+ case "None":
132
+ console.log("Profile not found")
133
+ break
134
+ }
135
+ ```
136
+
137
+ #### Array Operations
138
+
139
+ ```typescript
140
+ function first<T>(arr: T[]): Option<T> {
141
+ return arr.length > 0 ? Some(arr[0]) : None
142
+ }
143
+
144
+ function last<T>(arr: T[]): Option<T> {
145
+ return arr.length > 0 ? Some(arr[arr.length - 1]) : None
146
+ }
147
+
148
+ const numbers = [1, 2, 3, 4, 5]
149
+ const firstNum = first(numbers) // Some(1)
150
+ const lastNum = last(numbers) // Some(5)
151
+
152
+ const empty: number[] = []
153
+ const noFirst = first(empty) // None
154
+ ```
155
+
156
+ ## Result Type
157
+
158
+ Result represents "an operation that may fail with an error." It makes errors explicit in function signatures and forces error handling.
159
+
160
+ ### Definition
161
+
162
+ ```typescript
163
+ type Ok<T> = { _tag: "Ok"; value: T }
164
+ type Err<E> = { _tag: "Err"; error: E }
165
+ type Result<T, E> = Ok<T> | Err<E>
166
+
167
+ const Ok = <T>(value: T): Result<T, never> => ({ _tag: "Ok", value })
168
+ const Err = <E>(error: E): Result<never, E> => ({ _tag: "Err", error })
169
+ ```
170
+
171
+ ### Basic Usage
172
+
173
+ ```typescript
174
+ type ParseError = { field: string; message: string }
175
+
176
+ function parsePort(raw: unknown): Result<number, ParseError> {
177
+ if (typeof raw !== "number") {
178
+ return Err({ field: "port", message: "must be number" })
179
+ }
180
+ if (raw < 1 || raw > 65535) {
181
+ return Err({ field: "port", message: "must be 1-65535" })
182
+ }
183
+ return Ok(raw)
184
+ }
185
+
186
+ const result = parsePort(3000)
187
+ switch (result._tag) {
188
+ case "Ok":
189
+ console.log(`Port: ${result.value}`)
190
+ break
191
+ case "Err":
192
+ console.error(`Error in ${result.error.field}: ${result.error.message}`)
193
+ break
194
+ }
195
+ ```
196
+
197
+ ### When to Use Result
198
+
199
+ Use Result when:
200
+ - An operation may fail with recoverable errors
201
+ - You need to propagate error context (not just "failed")
202
+ - You want errors in function signatures (self-documenting)
203
+ - You want to avoid try/catch for expected failures
204
+
205
+ **Example scenarios:**
206
+ - Parsing/validating user input
207
+ - Network requests
208
+ - File I/O operations
209
+ - Business rule validation
210
+
211
+ ### Result Utilities
212
+
213
+ ```typescript
214
+ // Check variant
215
+ const isOk = <T, E>(result: Result<T, E>): result is Ok<T> => result._tag === "Ok"
216
+ const isErr = <T, E>(result: Result<T, E>): result is Err<E> => result._tag === "Err"
217
+
218
+ // Get value or throw
219
+ const unwrap = <T, E>(result: Result<T, E>): T => {
220
+ if (result._tag === "Ok") return result.value
221
+ throw new Error(`Unwrap failed: ${JSON.stringify(result.error)}`)
222
+ }
223
+
224
+ // Get value or default
225
+ const getOrElse = <T, E>(result: Result<T, E>, defaultValue: T): T =>
226
+ result._tag === "Ok" ? result.value : defaultValue
227
+
228
+ // Transform success value
229
+ const map = <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>
230
+ result._tag === "Ok" ? Ok(fn(result.value)) : result
231
+
232
+ // Transform error
233
+ const mapErr = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>
234
+ result._tag === "Err" ? Err(fn(result.error)) : result
235
+
236
+ // Chain operations that return Result
237
+ const flatMap = <T, U, E>(
238
+ result: Result<T, E>,
239
+ fn: (value: T) => Result<U, E>
240
+ ): Result<U, E> =>
241
+ result._tag === "Ok" ? fn(result.value) : result
242
+
243
+ // Combine multiple Results
244
+ const all = <T, E>(results: Result<T, E>[]): Result<T[], E> => {
245
+ const values: T[] = []
246
+ for (const result of results) {
247
+ if (result._tag === "Err") return result
248
+ values.push(result.value)
249
+ }
250
+ return Ok(values)
251
+ }
252
+ ```
253
+
254
+ ### Result Examples
255
+
256
+ #### Configuration Validation
257
+
258
+ ```typescript
259
+ type ConfigError = { field: string; message: string }
260
+
261
+ function parsePort(raw: unknown): Result<number, ConfigError> {
262
+ if (typeof raw !== "number") {
263
+ return Err({ field: "port", message: "must be number" })
264
+ }
265
+ if (raw < 1 || raw > 65535) {
266
+ return Err({ field: "port", message: "must be 1-65535" })
267
+ }
268
+ return Ok(raw)
269
+ }
270
+
271
+ function parseHost(raw: unknown): Result<string, ConfigError> {
272
+ if (typeof raw !== "string") {
273
+ return Err({ field: "host", message: "must be string" })
274
+ }
275
+ if (raw.length === 0) {
276
+ return Err({ field: "host", message: "cannot be empty" })
277
+ }
278
+ return Ok(raw)
279
+ }
280
+
281
+ type Config = { port: number; host: string }
282
+
283
+ function parseConfig(raw: unknown): Result<Config, ConfigError> {
284
+ const data = raw as any
285
+
286
+ const portResult = parsePort(data.port)
287
+ if (portResult._tag === "Err") return portResult
288
+
289
+ const hostResult = parseHost(data.host)
290
+ if (hostResult._tag === "Err") return hostResult
291
+
292
+ return Ok({ port: portResult.value, host: hostResult.value })
293
+ }
294
+
295
+ // Usage
296
+ const configResult = parseConfig({ port: 3000, host: "localhost" })
297
+ switch (configResult._tag) {
298
+ case "Ok":
299
+ startServer(configResult.value)
300
+ break
301
+ case "Err":
302
+ console.error(`Config error in ${configResult.error.field}: ${configResult.error.message}`)
303
+ process.exit(1)
304
+ }
305
+ ```
306
+
307
+ #### Chaining Operations
308
+
309
+ ```typescript
310
+ type DbError = { kind: "not_found" } | { kind: "connection_error"; message: string }
311
+
312
+ function findUser(id: string): Result<User, DbError> {
313
+ // ...
314
+ }
315
+
316
+ function updateUser(user: User, updates: Partial<User>): Result<User, DbError> {
317
+ // ...
318
+ }
319
+
320
+ // Chain without nested if statements
321
+ const result = flatMap(
322
+ findUser("123"),
323
+ (user) => updateUser(user, { name: "Alice" })
324
+ )
325
+
326
+ switch (result._tag) {
327
+ case "Ok":
328
+ console.log(`Updated user: ${result.value.name}`)
329
+ break
330
+ case "Err":
331
+ if (result.error.kind === "not_found") {
332
+ console.error("User not found")
333
+ } else {
334
+ console.error(`Connection error: ${result.error.message}`)
335
+ }
336
+ break
337
+ }
338
+ ```
339
+
340
+ #### HTTP Request Handling
341
+
342
+ ```typescript
343
+ type HttpError =
344
+ | { kind: "network"; message: string }
345
+ | { kind: "timeout" }
346
+ | { kind: "server"; statusCode: number }
347
+ | { kind: "parse"; message: string }
348
+
349
+ async function fetchUser(id: string): Promise<Result<User, HttpError>> {
350
+ try {
351
+ const response = await fetch(`/api/users/${id}`)
352
+
353
+ if (!response.ok) {
354
+ return Err({ kind: "server", statusCode: response.status })
355
+ }
356
+
357
+ const data = await response.json()
358
+ return Ok(data as User)
359
+ } catch (e) {
360
+ if (e instanceof SyntaxError) {
361
+ return Err({ kind: "parse", message: e.message })
362
+ }
363
+ return Err({ kind: "network", message: String(e) })
364
+ }
365
+ }
366
+
367
+ // Usage
368
+ const userResult = await fetchUser("123")
369
+ switch (userResult._tag) {
370
+ case "Ok":
371
+ renderUser(userResult.value)
372
+ break
373
+ case "Err":
374
+ switch (userResult.error.kind) {
375
+ case "network":
376
+ showError("Network error, please try again")
377
+ break
378
+ case "timeout":
379
+ showError("Request timed out")
380
+ break
381
+ case "server":
382
+ showError(`Server error: ${userResult.error.statusCode}`)
383
+ break
384
+ case "parse":
385
+ showError("Invalid response from server")
386
+ break
387
+ }
388
+ break
389
+ }
390
+ ```
391
+
392
+ #### Accumulating Multiple Errors
393
+
394
+ ```typescript
395
+ type ValidationError = { field: string; message: string }
396
+
397
+ function validateEmail(email: string): Result<string, ValidationError> {
398
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
399
+ return Err({ field: "email", message: "invalid email format" })
400
+ }
401
+ return Ok(email)
402
+ }
403
+
404
+ function validatePassword(password: string): Result<string, ValidationError> {
405
+ if (password.length < 8) {
406
+ return Err({ field: "password", message: "must be at least 8 characters" })
407
+ }
408
+ return Ok(password)
409
+ }
410
+
411
+ type SignupForm = { email: string; password: string }
412
+
413
+ function validateSignup(form: SignupForm): Result<SignupForm, ValidationError[]> {
414
+ const errors: ValidationError[] = []
415
+
416
+ const emailResult = validateEmail(form.email)
417
+ if (emailResult._tag === "Err") {
418
+ errors.push(emailResult.error)
419
+ }
420
+
421
+ const passwordResult = validatePassword(form.password)
422
+ if (passwordResult._tag === "Err") {
423
+ errors.push(passwordResult.error)
424
+ }
425
+
426
+ if (errors.length > 0) {
427
+ return Err(errors)
428
+ }
429
+
430
+ return Ok(form)
431
+ }
432
+ ```
433
+
434
+ ## Option vs Result vs Exceptions
435
+
436
+ ### Decision Guide
437
+
438
+ | Scenario | Use |
439
+ |----------|-----|
440
+ | Value may be absent (expected) | Option |
441
+ | Operation may fail (recoverable) | Result |
442
+ | Programmer error (bug) | Exception |
443
+
444
+ ### Examples
445
+
446
+ ```typescript
447
+ // Option: Value may be absent
448
+ function findUser(id: string): Option<User>
449
+
450
+ // Result: Operation may fail
451
+ function parseConfig(raw: string): Result<Config, ParseError>
452
+
453
+ // Exception: Programmer error
454
+ function unreachable(message: string): never {
455
+ throw new Error(`Unreachable: ${message}`)
456
+ }
457
+ ```
458
+
459
+ ### When to Use Exceptions
460
+
461
+ Use exceptions for:
462
+ - Programmer errors (bugs)
463
+ - Assertion failures
464
+ - Unrecoverable errors
465
+ - Contract violations
466
+
467
+ ```typescript
468
+ function assertNever(x: never): never {
469
+ throw new Error(`Unhandled variant: ${JSON.stringify(x)}`)
470
+ }
471
+
472
+ function divide(a: number, b: number): number {
473
+ if (b === 0) {
474
+ throw new Error("Division by zero") // Programmer error
475
+ }
476
+ return a / b
477
+ }
478
+ ```
479
+
480
+ ## Converting Between Types
481
+
482
+ ### Option to Result
483
+
484
+ ```typescript
485
+ function optionToResult<T, E>(opt: Option<T>, error: E): Result<T, E> {
486
+ return opt._tag === "Some" ? Ok(opt.value) : Err(error)
487
+ }
488
+
489
+ // Usage
490
+ const user = findUser("123")
491
+ const result = optionToResult(user, { kind: "not_found" })
492
+ ```
493
+
494
+ ### Result to Option
495
+
496
+ ```typescript
497
+ function resultToOption<T, E>(result: Result<T, E>): Option<T> {
498
+ return result._tag === "Ok" ? Some(result.value) : None
499
+ }
500
+
501
+ // Usage
502
+ const configResult = parseConfig(rawConfig)
503
+ const config = resultToOption(configResult) // Discard error details
504
+ ```
505
+
506
+ ### Null/Undefined to Option
507
+
508
+ ```typescript
509
+ function fromNullable<T>(value: T | null | undefined): Option<T> {
510
+ return value != null ? Some(value) : None
511
+ }
512
+
513
+ // Usage
514
+ const maybePort = fromNullable(process.env.PORT)
515
+ ```
516
+
517
+ ## Best Practices
518
+
519
+ 1. **Make errors explicit in signatures**: `Result<T, E>` tells callers what can fail
520
+ 2. **Use specific error types**: Don't use `string` or `Error` as error type
521
+ 3. **Combine with discriminated unions**: Error variants should be tagged unions
522
+ 4. **Avoid nested Results**: Use `flatMap` to chain operations
523
+ 5. **Don't mix Option and null**: Choose one approach per codebase
524
+ 6. **Use utilities for common patterns**: Don't rewrite `map`, `flatMap`, etc.
525
+ 7. **Document when to use exceptions**: Reserve for programmer errors
526
+
527
+ ## Common Patterns
528
+
529
+ ### Early Return with Result
530
+
531
+ ```typescript
532
+ function processOrder(orderId: string): Result<Order, OrderError> {
533
+ const orderResult = findOrder(orderId)
534
+ if (orderResult._tag === "Err") return orderResult
535
+
536
+ const paymentResult = processPayment(orderResult.value)
537
+ if (paymentResult._tag === "Err") return mapErr(paymentResult, toOrderError)
538
+
539
+ const shipmentResult = createShipment(orderResult.value)
540
+ if (shipmentResult._tag === "Err") return mapErr(shipmentResult, toOrderError)
541
+
542
+ return Ok(orderResult.value)
543
+ }
544
+ ```
545
+
546
+ ### Combining Multiple Results
547
+
548
+ ```typescript
549
+ function validateAll<T>(
550
+ validators: ((value: T) => Result<T, string>)[]
551
+ ): (value: T) => Result<T, string[]> {
552
+ return (value) => {
553
+ const errors: string[] = []
554
+
555
+ for (const validator of validators) {
556
+ const result = validator(value)
557
+ if (result._tag === "Err") {
558
+ errors.push(result.error)
559
+ }
560
+ }
561
+
562
+ return errors.length > 0 ? Err(errors) : Ok(value)
563
+ }
564
+ }
565
+ ```
566
+
567
+ ### Safe Property Access with Option
568
+
569
+ ```typescript
570
+ function getProp<T, K extends keyof T>(obj: T, key: K): Option<T[K]> {
571
+ const value = obj[key]
572
+ return value !== undefined ? Some(value) : None
573
+ }
574
+
575
+ // Usage
576
+ type Config = { port?: number }
577
+ const config: Config = {}
578
+ const port = getProp(config, "port") // Option<number>
579
+ ```
580
+
581
+ ## Further Reading
582
+
583
+ - [ADTs](./adts.md) - Algebraic Data Types and discriminated unions
584
+ - [Branded Types](./branded-types.md) - Smart constructors and nominal typing
585
+ - [Migration Guide](./migration-guide.md) - Step-by-step adoption playbook