@dojocho/effect-ts 0.0.1

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 (149) hide show
  1. package/DOJO.md +22 -0
  2. package/dojo.json +50 -0
  3. package/katas/001-hello-effect/SENSEI.md +72 -0
  4. package/katas/001-hello-effect/solution.test.ts +35 -0
  5. package/katas/001-hello-effect/solution.ts +16 -0
  6. package/katas/002-transform-with-map/SENSEI.md +72 -0
  7. package/katas/002-transform-with-map/solution.test.ts +33 -0
  8. package/katas/002-transform-with-map/solution.ts +16 -0
  9. package/katas/003-generator-pipelines/SENSEI.md +72 -0
  10. package/katas/003-generator-pipelines/solution.test.ts +40 -0
  11. package/katas/003-generator-pipelines/solution.ts +29 -0
  12. package/katas/004-flatmap-and-chaining/SENSEI.md +80 -0
  13. package/katas/004-flatmap-and-chaining/solution.test.ts +34 -0
  14. package/katas/004-flatmap-and-chaining/solution.ts +18 -0
  15. package/katas/005-pipe-composition/SENSEI.md +81 -0
  16. package/katas/005-pipe-composition/solution.test.ts +41 -0
  17. package/katas/005-pipe-composition/solution.ts +19 -0
  18. package/katas/006-handle-errors/SENSEI.md +86 -0
  19. package/katas/006-handle-errors/solution.test.ts +53 -0
  20. package/katas/006-handle-errors/solution.ts +30 -0
  21. package/katas/007-tagged-errors/SENSEI.md +79 -0
  22. package/katas/007-tagged-errors/solution.test.ts +82 -0
  23. package/katas/007-tagged-errors/solution.ts +37 -0
  24. package/katas/008-error-patterns/SENSEI.md +89 -0
  25. package/katas/008-error-patterns/solution.test.ts +41 -0
  26. package/katas/008-error-patterns/solution.ts +38 -0
  27. package/katas/009-option-type/SENSEI.md +96 -0
  28. package/katas/009-option-type/solution.test.ts +49 -0
  29. package/katas/009-option-type/solution.ts +26 -0
  30. package/katas/010-either-and-exit/SENSEI.md +86 -0
  31. package/katas/010-either-and-exit/solution.test.ts +33 -0
  32. package/katas/010-either-and-exit/solution.ts +17 -0
  33. package/katas/011-services-and-context/SENSEI.md +82 -0
  34. package/katas/011-services-and-context/solution.test.ts +23 -0
  35. package/katas/011-services-and-context/solution.ts +17 -0
  36. package/katas/012-layers/SENSEI.md +73 -0
  37. package/katas/012-layers/solution.test.ts +23 -0
  38. package/katas/012-layers/solution.ts +26 -0
  39. package/katas/013-testing-effects/SENSEI.md +88 -0
  40. package/katas/013-testing-effects/solution.test.ts +41 -0
  41. package/katas/013-testing-effects/solution.ts +20 -0
  42. package/katas/014-schema-basics/SENSEI.md +81 -0
  43. package/katas/014-schema-basics/solution.test.ts +35 -0
  44. package/katas/014-schema-basics/solution.ts +25 -0
  45. package/katas/015-domain-modeling/SENSEI.md +85 -0
  46. package/katas/015-domain-modeling/solution.test.ts +46 -0
  47. package/katas/015-domain-modeling/solution.ts +42 -0
  48. package/katas/016-retry-and-schedule/SENSEI.md +72 -0
  49. package/katas/016-retry-and-schedule/solution.test.ts +26 -0
  50. package/katas/016-retry-and-schedule/solution.ts +23 -0
  51. package/katas/017-parallel-effects/SENSEI.md +70 -0
  52. package/katas/017-parallel-effects/solution.test.ts +33 -0
  53. package/katas/017-parallel-effects/solution.ts +17 -0
  54. package/katas/018-race-and-timeout/SENSEI.md +75 -0
  55. package/katas/018-race-and-timeout/solution.test.ts +30 -0
  56. package/katas/018-race-and-timeout/solution.ts +27 -0
  57. package/katas/019-ref-and-state/SENSEI.md +72 -0
  58. package/katas/019-ref-and-state/solution.test.ts +29 -0
  59. package/katas/019-ref-and-state/solution.ts +16 -0
  60. package/katas/020-fibers/SENSEI.md +80 -0
  61. package/katas/020-fibers/solution.test.ts +23 -0
  62. package/katas/020-fibers/solution.ts +23 -0
  63. package/katas/021-acquire-release/SENSEI.md +57 -0
  64. package/katas/021-acquire-release/solution.test.ts +23 -0
  65. package/katas/021-acquire-release/solution.ts +22 -0
  66. package/katas/022-scoped-layers/SENSEI.md +52 -0
  67. package/katas/022-scoped-layers/solution.test.ts +35 -0
  68. package/katas/022-scoped-layers/solution.ts +19 -0
  69. package/katas/023-resource-patterns/SENSEI.md +52 -0
  70. package/katas/023-resource-patterns/solution.test.ts +20 -0
  71. package/katas/023-resource-patterns/solution.ts +13 -0
  72. package/katas/024-streams-basics/SENSEI.md +61 -0
  73. package/katas/024-streams-basics/solution.test.ts +30 -0
  74. package/katas/024-streams-basics/solution.ts +16 -0
  75. package/katas/025-stream-operations/SENSEI.md +59 -0
  76. package/katas/025-stream-operations/solution.test.ts +26 -0
  77. package/katas/025-stream-operations/solution.ts +17 -0
  78. package/katas/026-combining-streams/SENSEI.md +54 -0
  79. package/katas/026-combining-streams/solution.test.ts +20 -0
  80. package/katas/026-combining-streams/solution.ts +16 -0
  81. package/katas/027-data-pipelines/SENSEI.md +58 -0
  82. package/katas/027-data-pipelines/solution.test.ts +22 -0
  83. package/katas/027-data-pipelines/solution.ts +16 -0
  84. package/katas/028-logging-and-spans/SENSEI.md +58 -0
  85. package/katas/028-logging-and-spans/solution.test.ts +50 -0
  86. package/katas/028-logging-and-spans/solution.ts +20 -0
  87. package/katas/029-http-client/SENSEI.md +59 -0
  88. package/katas/029-http-client/solution.test.ts +49 -0
  89. package/katas/029-http-client/solution.ts +24 -0
  90. package/katas/030-capstone/SENSEI.md +63 -0
  91. package/katas/030-capstone/solution.test.ts +67 -0
  92. package/katas/030-capstone/solution.ts +55 -0
  93. package/katas/031-config-and-environment/SENSEI.md +77 -0
  94. package/katas/031-config-and-environment/solution.test.ts +38 -0
  95. package/katas/031-config-and-environment/solution.ts +11 -0
  96. package/katas/032-cause-and-defects/SENSEI.md +90 -0
  97. package/katas/032-cause-and-defects/solution.test.ts +50 -0
  98. package/katas/032-cause-and-defects/solution.ts +23 -0
  99. package/katas/033-pattern-matching/SENSEI.md +86 -0
  100. package/katas/033-pattern-matching/solution.test.ts +36 -0
  101. package/katas/033-pattern-matching/solution.ts +28 -0
  102. package/katas/034-deferred-and-coordination/SENSEI.md +85 -0
  103. package/katas/034-deferred-and-coordination/solution.test.ts +25 -0
  104. package/katas/034-deferred-and-coordination/solution.ts +24 -0
  105. package/katas/035-queue-and-backpressure/SENSEI.md +100 -0
  106. package/katas/035-queue-and-backpressure/solution.test.ts +25 -0
  107. package/katas/035-queue-and-backpressure/solution.ts +21 -0
  108. package/katas/036-schema-advanced/SENSEI.md +81 -0
  109. package/katas/036-schema-advanced/solution.test.ts +55 -0
  110. package/katas/036-schema-advanced/solution.ts +19 -0
  111. package/katas/037-cache-and-memoization/SENSEI.md +73 -0
  112. package/katas/037-cache-and-memoization/solution.test.ts +47 -0
  113. package/katas/037-cache-and-memoization/solution.ts +24 -0
  114. package/katas/038-metrics/SENSEI.md +91 -0
  115. package/katas/038-metrics/solution.test.ts +39 -0
  116. package/katas/038-metrics/solution.ts +23 -0
  117. package/katas/039-managed-runtime/SENSEI.md +75 -0
  118. package/katas/039-managed-runtime/solution.test.ts +29 -0
  119. package/katas/039-managed-runtime/solution.ts +19 -0
  120. package/katas/040-request-batching/SENSEI.md +87 -0
  121. package/katas/040-request-batching/solution.test.ts +56 -0
  122. package/katas/040-request-batching/solution.ts +32 -0
  123. package/package.json +22 -0
  124. package/skills/effect-patterns-building-apis/SKILL.md +2393 -0
  125. package/skills/effect-patterns-building-data-pipelines/SKILL.md +1876 -0
  126. package/skills/effect-patterns-concurrency/SKILL.md +2999 -0
  127. package/skills/effect-patterns-concurrency-getting-started/SKILL.md +351 -0
  128. package/skills/effect-patterns-core-concepts/SKILL.md +3199 -0
  129. package/skills/effect-patterns-domain-modeling/SKILL.md +1385 -0
  130. package/skills/effect-patterns-error-handling/SKILL.md +1212 -0
  131. package/skills/effect-patterns-error-handling-resilience/SKILL.md +179 -0
  132. package/skills/effect-patterns-error-management/SKILL.md +1668 -0
  133. package/skills/effect-patterns-getting-started/SKILL.md +237 -0
  134. package/skills/effect-patterns-making-http-requests/SKILL.md +1756 -0
  135. package/skills/effect-patterns-observability/SKILL.md +1586 -0
  136. package/skills/effect-patterns-platform/SKILL.md +1195 -0
  137. package/skills/effect-patterns-platform-getting-started/SKILL.md +179 -0
  138. package/skills/effect-patterns-project-setup--execution/SKILL.md +233 -0
  139. package/skills/effect-patterns-resource-management/SKILL.md +827 -0
  140. package/skills/effect-patterns-scheduling/SKILL.md +451 -0
  141. package/skills/effect-patterns-scheduling-periodic-tasks/SKILL.md +763 -0
  142. package/skills/effect-patterns-streams/SKILL.md +2052 -0
  143. package/skills/effect-patterns-streams-getting-started/SKILL.md +421 -0
  144. package/skills/effect-patterns-streams-sinks/SKILL.md +1181 -0
  145. package/skills/effect-patterns-testing/SKILL.md +1632 -0
  146. package/skills/effect-patterns-tooling-and-debugging/SKILL.md +1125 -0
  147. package/skills/effect-patterns-value-handling/SKILL.md +676 -0
  148. package/tsconfig.json +20 -0
  149. package/vitest.config.ts +3 -0
@@ -0,0 +1,351 @@
1
+ ---
2
+ name: effect-patterns-concurrency-getting-started
3
+ description: Effect-TS patterns for Concurrency Getting Started. Use when working with concurrency getting started in Effect-TS applications.
4
+ ---
5
+ # Effect-TS Patterns: Concurrency Getting Started
6
+ This skill provides 3 curated Effect-TS patterns for concurrency getting started.
7
+ Use this skill when working on tasks related to:
8
+ - concurrency getting started
9
+ - Best practices in Effect-TS applications
10
+ - Real-world patterns and solutions
11
+
12
+ ---
13
+
14
+ ## 🟢 Beginner Patterns
15
+
16
+ ### Race Effects and Handle Timeouts
17
+
18
+ **Rule:** Use Effect.race for fastest-wins, Effect.timeout for time limits.
19
+
20
+ **Good Example:**
21
+
22
+ ```typescript
23
+ import { Effect, Option } from "effect"
24
+
25
+ // ============================================
26
+ // BASIC RACE: First one wins
27
+ // ============================================
28
+
29
+ const server1 = Effect.gen(function* () {
30
+ yield* Effect.sleep("100 millis")
31
+ return "Response from server 1"
32
+ })
33
+
34
+ const server2 = Effect.gen(function* () {
35
+ yield* Effect.sleep("50 millis")
36
+ return "Response from server 2"
37
+ })
38
+
39
+ const raceServers = Effect.race(server1, server2)
40
+
41
+ Effect.runPromise(raceServers).then((result) => {
42
+ console.log(result) // "Response from server 2" (faster)
43
+ })
44
+
45
+ // ============================================
46
+ // BASIC TIMEOUT: Limit execution time
47
+ // ============================================
48
+
49
+ const slowOperation = Effect.gen(function* () {
50
+ yield* Effect.sleep("5 seconds")
51
+ return "Finally done"
52
+ })
53
+
54
+ // Returns Option.none if timeout
55
+ const withTimeout = slowOperation.pipe(
56
+ Effect.timeout("1 second")
57
+ )
58
+
59
+ Effect.runPromise(withTimeout).then((result) => {
60
+ if (Option.isNone(result)) {
61
+ console.log("Operation timed out")
62
+ } else {
63
+ console.log(`Got: ${result.value}`)
64
+ }
65
+ })
66
+
67
+ // ============================================
68
+ // TIMEOUT WITH FALLBACK
69
+ // ============================================
70
+
71
+ const withFallback = slowOperation.pipe(
72
+ Effect.timeoutTo({
73
+ duration: "1 second",
74
+ onTimeout: () => Effect.succeed("Using cached value"),
75
+ })
76
+ )
77
+
78
+ Effect.runPromise(withFallback).then((result) => {
79
+ console.log(result) // "Using cached value"
80
+ })
81
+
82
+ // ============================================
83
+ // TIMEOUT FAIL: Throw error on timeout
84
+ // ============================================
85
+
86
+ class TimeoutError {
87
+ readonly _tag = "TimeoutError"
88
+ }
89
+
90
+ const failOnTimeout = slowOperation.pipe(
91
+ Effect.timeoutFail({
92
+ duration: "1 second",
93
+ onTimeout: () => new TimeoutError(),
94
+ })
95
+ )
96
+
97
+ // ============================================
98
+ // RACE ALL: Multiple competing effects
99
+ // ============================================
100
+
101
+ const fetchFromCache = Effect.gen(function* () {
102
+ yield* Effect.sleep("10 millis")
103
+ return { source: "cache", data: "cached data" }
104
+ })
105
+
106
+ const fetchFromDB = Effect.gen(function* () {
107
+ yield* Effect.sleep("100 millis")
108
+ return { source: "db", data: "fresh data" }
109
+ })
110
+
111
+ const fetchFromAPI = Effect.gen(function* () {
112
+ yield* Effect.sleep("200 millis")
113
+ return { source: "api", data: "api data" }
114
+ })
115
+
116
+ const raceAll = Effect.raceAll([fetchFromCache, fetchFromDB, fetchFromAPI])
117
+
118
+ Effect.runPromise(raceAll).then((result) => {
119
+ console.log(`Winner: ${result.source}`) // "cache"
120
+ })
121
+
122
+ // ============================================
123
+ // PRACTICAL: API with timeout and fallback
124
+ // ============================================
125
+
126
+ const fetchWithResilience = (url: string) =>
127
+ Effect.gen(function* () {
128
+ const response = yield* Effect.tryPromise(() =>
129
+ fetch(url).then((r) => r.json())
130
+ ).pipe(
131
+ Effect.timeout("3 seconds"),
132
+ Effect.flatMap((opt) =>
133
+ Option.isSome(opt)
134
+ ? Effect.succeed(opt.value)
135
+ : Effect.succeed({ error: "timeout", cached: true })
136
+ )
137
+ )
138
+
139
+ return response
140
+ })
141
+ ```
142
+
143
+ **Rationale:**
144
+
145
+ Use `Effect.race` when you want the first result from competing effects. Use `Effect.timeout` to limit how long an effect can run.
146
+
147
+ ---
148
+
149
+
150
+ Racing and timeouts prevent your app from hanging:
151
+
152
+ 1. **Redundant requests** - Race multiple servers, use fastest response
153
+ 2. **Timeouts** - Fail fast if operation takes too long
154
+ 3. **Fallbacks** - Try fast path, fall back to slow path
155
+
156
+ ---
157
+
158
+ ---
159
+
160
+ ### Understanding Fibers
161
+
162
+ **Rule:** Fibers are lightweight threads managed by Effect, enabling efficient concurrency without OS thread overhead.
163
+
164
+ **Good Example:**
165
+
166
+ ```typescript
167
+ import { Effect, Fiber } from "effect"
168
+
169
+ // ============================================
170
+ // WHAT IS A FIBER?
171
+ // ============================================
172
+
173
+ // A fiber is a running effect. When you run an effect,
174
+ // it executes on a fiber.
175
+
176
+ const myEffect = Effect.gen(function* () {
177
+ yield* Effect.log("Hello from a fiber!")
178
+ yield* Effect.sleep("100 millis")
179
+ return 42
180
+ })
181
+
182
+ // This runs myEffect on the "main" fiber
183
+ Effect.runPromise(myEffect)
184
+
185
+ // ============================================
186
+ // FORKING: Create a new fiber
187
+ // ============================================
188
+
189
+ const withFork = Effect.gen(function* () {
190
+ yield* Effect.log("Main fiber starting")
191
+
192
+ // Fork creates a new fiber that runs independently
193
+ const fiber = yield* Effect.fork(
194
+ Effect.gen(function* () {
195
+ yield* Effect.log("Child fiber running")
196
+ yield* Effect.sleep("200 millis")
197
+ yield* Effect.log("Child fiber done")
198
+ return "child result"
199
+ })
200
+ )
201
+
202
+ yield* Effect.log("Main fiber continues immediately")
203
+ yield* Effect.sleep("100 millis")
204
+ yield* Effect.log("Main fiber waiting for child...")
205
+
206
+ // Wait for the forked fiber to complete
207
+ const result = yield* Fiber.join(fiber)
208
+ yield* Effect.log(`Got result: ${result}`)
209
+ })
210
+
211
+ Effect.runPromise(withFork)
212
+ /*
213
+ Output:
214
+ Main fiber starting
215
+ Child fiber running
216
+ Main fiber continues immediately
217
+ Main fiber waiting for child...
218
+ Child fiber done
219
+ Got result: child result
220
+ */
221
+
222
+ // ============================================
223
+ // FIBER OPERATIONS
224
+ // ============================================
225
+
226
+ const fiberOps = Effect.gen(function* () {
227
+ const fiber = yield* Effect.fork(
228
+ Effect.gen(function* () {
229
+ yield* Effect.sleep("1 second")
230
+ return "done"
231
+ })
232
+ )
233
+
234
+ // Check if fiber is done (non-blocking)
235
+ const poll = yield* Fiber.poll(fiber)
236
+ yield* Effect.log(`Poll result: ${poll}`) // None (still running)
237
+
238
+ // Wait for completion
239
+ const result = yield* Fiber.join(fiber)
240
+ yield* Effect.log(`Join result: ${result}`)
241
+
242
+ // Or interrupt if taking too long
243
+ // yield* Fiber.interrupt(fiber)
244
+ })
245
+ ```
246
+
247
+ **Rationale:**
248
+
249
+ Fibers are Effect's lightweight threads. They're cheap to create (thousands are fine), automatically managed, and can be interrupted cleanly.
250
+
251
+ ---
252
+
253
+
254
+ Unlike OS threads:
255
+
256
+ 1. **Lightweight** - Create thousands without performance issues
257
+ 2. **Cooperative** - Yield control at effect boundaries
258
+ 3. **Interruptible** - Can be cancelled cleanly
259
+ 4. **Structured** - Parent fibers manage children
260
+
261
+ ---
262
+
263
+ ---
264
+
265
+ ### Your First Parallel Operation
266
+
267
+ **Rule:** Use Effect.all with concurrency option to run independent effects in parallel.
268
+
269
+ **Good Example:**
270
+
271
+ ```typescript
272
+ import { Effect } from "effect"
273
+
274
+ // Simulate async operations
275
+ const fetchUser = Effect.gen(function* () {
276
+ yield* Effect.sleep("100 millis")
277
+ return { id: 1, name: "Alice" }
278
+ })
279
+
280
+ const fetchProducts = Effect.gen(function* () {
281
+ yield* Effect.sleep("150 millis")
282
+ return [{ id: 1, name: "Widget" }, { id: 2, name: "Gadget" }]
283
+ })
284
+
285
+ const fetchCart = Effect.gen(function* () {
286
+ yield* Effect.sleep("80 millis")
287
+ return { items: 3, total: 99.99 }
288
+ })
289
+
290
+ // ============================================
291
+ // SEQUENTIAL: One after another (~330ms)
292
+ // ============================================
293
+
294
+ const sequential = Effect.all([fetchUser, fetchProducts, fetchCart])
295
+
296
+ // ============================================
297
+ // PARALLEL: All at once (~150ms)
298
+ // ============================================
299
+
300
+ const parallel = Effect.all(
301
+ [fetchUser, fetchProducts, fetchCart],
302
+ { concurrency: "unbounded" }
303
+ )
304
+
305
+ // ============================================
306
+ // PARALLEL WITH LIMIT: Max 2 at a time
307
+ // ============================================
308
+
309
+ const limited = Effect.all(
310
+ [fetchUser, fetchProducts, fetchCart],
311
+ { concurrency: 2 }
312
+ )
313
+
314
+ // ============================================
315
+ // DEMO
316
+ // ============================================
317
+
318
+ const demo = Effect.gen(function* () {
319
+ const start = Date.now()
320
+
321
+ const [user, products, cart] = yield* parallel
322
+
323
+ const elapsed = Date.now() - start
324
+ yield* Effect.log(`Fetched in ${elapsed}ms`)
325
+ yield* Effect.log(`User: ${user.name}`)
326
+ yield* Effect.log(`Products: ${products.length}`)
327
+ yield* Effect.log(`Cart total: $${cart.total}`)
328
+ })
329
+
330
+ Effect.runPromise(demo)
331
+ // Output: Fetched in ~150ms (not ~330ms!)
332
+ ```
333
+
334
+ **Rationale:**
335
+
336
+ Use `Effect.all` with `{ concurrency: "unbounded" }` to run independent effects in parallel. Without the option, effects run sequentially.
337
+
338
+ ---
339
+
340
+
341
+ Parallel execution speeds up independent operations:
342
+
343
+ 1. **Fetch multiple APIs** - Get user, products, cart simultaneously
344
+ 2. **Process files** - Read multiple files at once
345
+ 3. **Database queries** - Run independent queries in parallel
346
+
347
+ ---
348
+
349
+ ---
350
+
351
+