@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.
- package/DOJO.md +22 -0
- package/dojo.json +50 -0
- package/katas/001-hello-effect/SENSEI.md +72 -0
- package/katas/001-hello-effect/solution.test.ts +35 -0
- package/katas/001-hello-effect/solution.ts +16 -0
- package/katas/002-transform-with-map/SENSEI.md +72 -0
- package/katas/002-transform-with-map/solution.test.ts +33 -0
- package/katas/002-transform-with-map/solution.ts +16 -0
- package/katas/003-generator-pipelines/SENSEI.md +72 -0
- package/katas/003-generator-pipelines/solution.test.ts +40 -0
- package/katas/003-generator-pipelines/solution.ts +29 -0
- package/katas/004-flatmap-and-chaining/SENSEI.md +80 -0
- package/katas/004-flatmap-and-chaining/solution.test.ts +34 -0
- package/katas/004-flatmap-and-chaining/solution.ts +18 -0
- package/katas/005-pipe-composition/SENSEI.md +81 -0
- package/katas/005-pipe-composition/solution.test.ts +41 -0
- package/katas/005-pipe-composition/solution.ts +19 -0
- package/katas/006-handle-errors/SENSEI.md +86 -0
- package/katas/006-handle-errors/solution.test.ts +53 -0
- package/katas/006-handle-errors/solution.ts +30 -0
- package/katas/007-tagged-errors/SENSEI.md +79 -0
- package/katas/007-tagged-errors/solution.test.ts +82 -0
- package/katas/007-tagged-errors/solution.ts +37 -0
- package/katas/008-error-patterns/SENSEI.md +89 -0
- package/katas/008-error-patterns/solution.test.ts +41 -0
- package/katas/008-error-patterns/solution.ts +38 -0
- package/katas/009-option-type/SENSEI.md +96 -0
- package/katas/009-option-type/solution.test.ts +49 -0
- package/katas/009-option-type/solution.ts +26 -0
- package/katas/010-either-and-exit/SENSEI.md +86 -0
- package/katas/010-either-and-exit/solution.test.ts +33 -0
- package/katas/010-either-and-exit/solution.ts +17 -0
- package/katas/011-services-and-context/SENSEI.md +82 -0
- package/katas/011-services-and-context/solution.test.ts +23 -0
- package/katas/011-services-and-context/solution.ts +17 -0
- package/katas/012-layers/SENSEI.md +73 -0
- package/katas/012-layers/solution.test.ts +23 -0
- package/katas/012-layers/solution.ts +26 -0
- package/katas/013-testing-effects/SENSEI.md +88 -0
- package/katas/013-testing-effects/solution.test.ts +41 -0
- package/katas/013-testing-effects/solution.ts +20 -0
- package/katas/014-schema-basics/SENSEI.md +81 -0
- package/katas/014-schema-basics/solution.test.ts +35 -0
- package/katas/014-schema-basics/solution.ts +25 -0
- package/katas/015-domain-modeling/SENSEI.md +85 -0
- package/katas/015-domain-modeling/solution.test.ts +46 -0
- package/katas/015-domain-modeling/solution.ts +42 -0
- package/katas/016-retry-and-schedule/SENSEI.md +72 -0
- package/katas/016-retry-and-schedule/solution.test.ts +26 -0
- package/katas/016-retry-and-schedule/solution.ts +23 -0
- package/katas/017-parallel-effects/SENSEI.md +70 -0
- package/katas/017-parallel-effects/solution.test.ts +33 -0
- package/katas/017-parallel-effects/solution.ts +17 -0
- package/katas/018-race-and-timeout/SENSEI.md +75 -0
- package/katas/018-race-and-timeout/solution.test.ts +30 -0
- package/katas/018-race-and-timeout/solution.ts +27 -0
- package/katas/019-ref-and-state/SENSEI.md +72 -0
- package/katas/019-ref-and-state/solution.test.ts +29 -0
- package/katas/019-ref-and-state/solution.ts +16 -0
- package/katas/020-fibers/SENSEI.md +80 -0
- package/katas/020-fibers/solution.test.ts +23 -0
- package/katas/020-fibers/solution.ts +23 -0
- package/katas/021-acquire-release/SENSEI.md +57 -0
- package/katas/021-acquire-release/solution.test.ts +23 -0
- package/katas/021-acquire-release/solution.ts +22 -0
- package/katas/022-scoped-layers/SENSEI.md +52 -0
- package/katas/022-scoped-layers/solution.test.ts +35 -0
- package/katas/022-scoped-layers/solution.ts +19 -0
- package/katas/023-resource-patterns/SENSEI.md +52 -0
- package/katas/023-resource-patterns/solution.test.ts +20 -0
- package/katas/023-resource-patterns/solution.ts +13 -0
- package/katas/024-streams-basics/SENSEI.md +61 -0
- package/katas/024-streams-basics/solution.test.ts +30 -0
- package/katas/024-streams-basics/solution.ts +16 -0
- package/katas/025-stream-operations/SENSEI.md +59 -0
- package/katas/025-stream-operations/solution.test.ts +26 -0
- package/katas/025-stream-operations/solution.ts +17 -0
- package/katas/026-combining-streams/SENSEI.md +54 -0
- package/katas/026-combining-streams/solution.test.ts +20 -0
- package/katas/026-combining-streams/solution.ts +16 -0
- package/katas/027-data-pipelines/SENSEI.md +58 -0
- package/katas/027-data-pipelines/solution.test.ts +22 -0
- package/katas/027-data-pipelines/solution.ts +16 -0
- package/katas/028-logging-and-spans/SENSEI.md +58 -0
- package/katas/028-logging-and-spans/solution.test.ts +50 -0
- package/katas/028-logging-and-spans/solution.ts +20 -0
- package/katas/029-http-client/SENSEI.md +59 -0
- package/katas/029-http-client/solution.test.ts +49 -0
- package/katas/029-http-client/solution.ts +24 -0
- package/katas/030-capstone/SENSEI.md +63 -0
- package/katas/030-capstone/solution.test.ts +67 -0
- package/katas/030-capstone/solution.ts +55 -0
- package/katas/031-config-and-environment/SENSEI.md +77 -0
- package/katas/031-config-and-environment/solution.test.ts +38 -0
- package/katas/031-config-and-environment/solution.ts +11 -0
- package/katas/032-cause-and-defects/SENSEI.md +90 -0
- package/katas/032-cause-and-defects/solution.test.ts +50 -0
- package/katas/032-cause-and-defects/solution.ts +23 -0
- package/katas/033-pattern-matching/SENSEI.md +86 -0
- package/katas/033-pattern-matching/solution.test.ts +36 -0
- package/katas/033-pattern-matching/solution.ts +28 -0
- package/katas/034-deferred-and-coordination/SENSEI.md +85 -0
- package/katas/034-deferred-and-coordination/solution.test.ts +25 -0
- package/katas/034-deferred-and-coordination/solution.ts +24 -0
- package/katas/035-queue-and-backpressure/SENSEI.md +100 -0
- package/katas/035-queue-and-backpressure/solution.test.ts +25 -0
- package/katas/035-queue-and-backpressure/solution.ts +21 -0
- package/katas/036-schema-advanced/SENSEI.md +81 -0
- package/katas/036-schema-advanced/solution.test.ts +55 -0
- package/katas/036-schema-advanced/solution.ts +19 -0
- package/katas/037-cache-and-memoization/SENSEI.md +73 -0
- package/katas/037-cache-and-memoization/solution.test.ts +47 -0
- package/katas/037-cache-and-memoization/solution.ts +24 -0
- package/katas/038-metrics/SENSEI.md +91 -0
- package/katas/038-metrics/solution.test.ts +39 -0
- package/katas/038-metrics/solution.ts +23 -0
- package/katas/039-managed-runtime/SENSEI.md +75 -0
- package/katas/039-managed-runtime/solution.test.ts +29 -0
- package/katas/039-managed-runtime/solution.ts +19 -0
- package/katas/040-request-batching/SENSEI.md +87 -0
- package/katas/040-request-batching/solution.test.ts +56 -0
- package/katas/040-request-batching/solution.ts +32 -0
- package/package.json +22 -0
- package/skills/effect-patterns-building-apis/SKILL.md +2393 -0
- package/skills/effect-patterns-building-data-pipelines/SKILL.md +1876 -0
- package/skills/effect-patterns-concurrency/SKILL.md +2999 -0
- package/skills/effect-patterns-concurrency-getting-started/SKILL.md +351 -0
- package/skills/effect-patterns-core-concepts/SKILL.md +3199 -0
- package/skills/effect-patterns-domain-modeling/SKILL.md +1385 -0
- package/skills/effect-patterns-error-handling/SKILL.md +1212 -0
- package/skills/effect-patterns-error-handling-resilience/SKILL.md +179 -0
- package/skills/effect-patterns-error-management/SKILL.md +1668 -0
- package/skills/effect-patterns-getting-started/SKILL.md +237 -0
- package/skills/effect-patterns-making-http-requests/SKILL.md +1756 -0
- package/skills/effect-patterns-observability/SKILL.md +1586 -0
- package/skills/effect-patterns-platform/SKILL.md +1195 -0
- package/skills/effect-patterns-platform-getting-started/SKILL.md +179 -0
- package/skills/effect-patterns-project-setup--execution/SKILL.md +233 -0
- package/skills/effect-patterns-resource-management/SKILL.md +827 -0
- package/skills/effect-patterns-scheduling/SKILL.md +451 -0
- package/skills/effect-patterns-scheduling-periodic-tasks/SKILL.md +763 -0
- package/skills/effect-patterns-streams/SKILL.md +2052 -0
- package/skills/effect-patterns-streams-getting-started/SKILL.md +421 -0
- package/skills/effect-patterns-streams-sinks/SKILL.md +1181 -0
- package/skills/effect-patterns-testing/SKILL.md +1632 -0
- package/skills/effect-patterns-tooling-and-debugging/SKILL.md +1125 -0
- package/skills/effect-patterns-value-handling/SKILL.md +676 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effect-patterns-streams-getting-started
|
|
3
|
+
description: Effect-TS patterns for Streams Getting Started. Use when working with streams getting started in Effect-TS applications.
|
|
4
|
+
---
|
|
5
|
+
# Effect-TS Patterns: Streams Getting Started
|
|
6
|
+
This skill provides 4 curated Effect-TS patterns for streams getting started.
|
|
7
|
+
Use this skill when working on tasks related to:
|
|
8
|
+
- streams getting started
|
|
9
|
+
- Best practices in Effect-TS applications
|
|
10
|
+
- Real-world patterns and solutions
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🟢 Beginner Patterns
|
|
15
|
+
|
|
16
|
+
### Your First Stream
|
|
17
|
+
|
|
18
|
+
**Rule:** Use Stream to process sequences of data lazily and efficiently.
|
|
19
|
+
|
|
20
|
+
**Good Example:**
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Effect, Stream } from "effect"
|
|
24
|
+
|
|
25
|
+
// Create a stream from explicit values
|
|
26
|
+
const numbers = Stream.make(1, 2, 3, 4, 5)
|
|
27
|
+
|
|
28
|
+
// Create a stream from an array
|
|
29
|
+
const fromArray = Stream.fromIterable([10, 20, 30])
|
|
30
|
+
|
|
31
|
+
// Create a single-value stream
|
|
32
|
+
const single = Stream.succeed("hello")
|
|
33
|
+
|
|
34
|
+
// Transform and run the stream
|
|
35
|
+
const program = numbers.pipe(
|
|
36
|
+
Stream.map((n) => n * 2), // Double each number
|
|
37
|
+
Stream.filter((n) => n > 4), // Keep only > 4
|
|
38
|
+
Stream.runCollect // Collect results
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
Effect.runPromise(program).then((chunk) => {
|
|
42
|
+
console.log([...chunk]) // [6, 8, 10]
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Anti-Pattern:**
|
|
47
|
+
|
|
48
|
+
Don't use regular arrays when you need lazy processing or async operations:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// Anti-pattern: Eager processing, all in memory
|
|
52
|
+
const numbers = [1, 2, 3, 4, 5]
|
|
53
|
+
const doubled = numbers.map((n) => n * 2)
|
|
54
|
+
const filtered = doubled.filter((n) => n > 4)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This loads everything into memory immediately. Use Stream when:
|
|
58
|
+
- Data is large or potentially infinite
|
|
59
|
+
- Data arrives asynchronously
|
|
60
|
+
- You need backpressure or resource management
|
|
61
|
+
|
|
62
|
+
**Rationale:**
|
|
63
|
+
|
|
64
|
+
A Stream is a lazy sequence of values that can be processed one at a time. Create streams with `Stream.make`, `Stream.fromIterable`, or `Stream.succeed`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Streams are Effect's answer to processing sequences of data. Unlike arrays which hold all values in memory at once, streams produce values on demand. This makes them ideal for:
|
|
70
|
+
|
|
71
|
+
1. **Large datasets** - Process millions of records without loading everything into memory
|
|
72
|
+
2. **Async data** - Handle data that arrives over time (files, APIs, events)
|
|
73
|
+
3. **Composable pipelines** - Chain transformations that work element by element
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### Stream vs Effect - When to Use Which
|
|
80
|
+
|
|
81
|
+
**Rule:** Use Effect for single values, Stream for sequences of values.
|
|
82
|
+
|
|
83
|
+
**Good Example:**
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { Effect, Stream } from "effect"
|
|
87
|
+
|
|
88
|
+
// ============================================
|
|
89
|
+
// EFFECT: Single result operations
|
|
90
|
+
// ============================================
|
|
91
|
+
|
|
92
|
+
// Fetch one user - returns Effect<User>
|
|
93
|
+
const fetchUser = (id: string) =>
|
|
94
|
+
Effect.tryPromise(() =>
|
|
95
|
+
fetch(`/api/users/${id}`).then((r) => r.json())
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// Read entire config - returns Effect<Config>
|
|
99
|
+
const loadConfig = Effect.tryPromise(() =>
|
|
100
|
+
fetch("/config.json").then((r) => r.json())
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
// ============================================
|
|
104
|
+
// STREAM: Multiple values operations
|
|
105
|
+
// ============================================
|
|
106
|
+
|
|
107
|
+
// Process file line by line - returns Stream<string>
|
|
108
|
+
const fileLines = Stream.fromIterable([
|
|
109
|
+
"line 1",
|
|
110
|
+
"line 2",
|
|
111
|
+
"line 3",
|
|
112
|
+
])
|
|
113
|
+
|
|
114
|
+
// Generate events over time - returns Stream<Event>
|
|
115
|
+
const events = Stream.make(
|
|
116
|
+
{ type: "click", x: 10 },
|
|
117
|
+
{ type: "click", x: 20 },
|
|
118
|
+
{ type: "scroll", y: 100 },
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
// ============================================
|
|
122
|
+
// CONVERTING BETWEEN THEM
|
|
123
|
+
// ============================================
|
|
124
|
+
|
|
125
|
+
// Effect → Stream (single value becomes 1-element stream)
|
|
126
|
+
const effectToStream = Stream.fromEffect(fetchUser("123"))
|
|
127
|
+
|
|
128
|
+
// Stream → Effect (collect all values into array)
|
|
129
|
+
const streamToEffect = Stream.runCollect(fileLines)
|
|
130
|
+
|
|
131
|
+
// Stream → Effect (process each value for side effects)
|
|
132
|
+
const processAll = fileLines.pipe(
|
|
133
|
+
Stream.runForEach((line) => Effect.log(`Processing: ${line}`))
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
// ============================================
|
|
137
|
+
// DECISION GUIDE
|
|
138
|
+
// ============================================
|
|
139
|
+
|
|
140
|
+
// Use Effect when:
|
|
141
|
+
// - Fetching a single resource
|
|
142
|
+
// - Computing a single result
|
|
143
|
+
// - Performing one action
|
|
144
|
+
|
|
145
|
+
// Use Stream when:
|
|
146
|
+
// - Reading files line by line
|
|
147
|
+
// - Processing paginated API results
|
|
148
|
+
// - Handling real-time events
|
|
149
|
+
// - Processing large datasets
|
|
150
|
+
// - Building data pipelines
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Rationale:**
|
|
154
|
+
|
|
155
|
+
Use `Effect` when your operation produces a single result. Use `Stream` when your operation produces multiple values over time.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
Both Effect and Stream are lazy and composable, but they serve different purposes:
|
|
161
|
+
|
|
162
|
+
| Aspect | Effect | Stream |
|
|
163
|
+
|--------|--------|--------|
|
|
164
|
+
| **Produces** | One value | Zero or more values |
|
|
165
|
+
| **Memory** | Holds one result | Processes incrementally |
|
|
166
|
+
| **Use case** | API call, DB query | File lines, events, batches |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### Running and Collecting Stream Results
|
|
173
|
+
|
|
174
|
+
**Rule:** Choose the right Stream.run* method based on what you need from the results.
|
|
175
|
+
|
|
176
|
+
**Good Example:**
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { Effect, Stream, Option } from "effect"
|
|
180
|
+
|
|
181
|
+
const numbers = Stream.make(1, 2, 3, 4, 5)
|
|
182
|
+
|
|
183
|
+
// ============================================
|
|
184
|
+
// runCollect - Get all results as a Chunk
|
|
185
|
+
// ============================================
|
|
186
|
+
|
|
187
|
+
const collectAll = numbers.pipe(
|
|
188
|
+
Stream.map((n) => n * 10),
|
|
189
|
+
Stream.runCollect
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
Effect.runPromise(collectAll).then((chunk) => {
|
|
193
|
+
console.log([...chunk]) // [10, 20, 30, 40, 50]
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// ============================================
|
|
197
|
+
// runForEach - Process each item
|
|
198
|
+
// ============================================
|
|
199
|
+
|
|
200
|
+
const processEach = numbers.pipe(
|
|
201
|
+
Stream.runForEach((n) =>
|
|
202
|
+
Effect.log(`Processing: ${n}`)
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
Effect.runPromise(processEach)
|
|
207
|
+
// Logs: Processing: 1, Processing: 2, etc.
|
|
208
|
+
|
|
209
|
+
// ============================================
|
|
210
|
+
// runDrain - Run for side effects only
|
|
211
|
+
// ============================================
|
|
212
|
+
|
|
213
|
+
const withSideEffects = numbers.pipe(
|
|
214
|
+
Stream.tap((n) => Effect.log(`Saw: ${n}`)),
|
|
215
|
+
Stream.runDrain // Discard values, just run
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
// ============================================
|
|
219
|
+
// runHead - Get first value only
|
|
220
|
+
// ============================================
|
|
221
|
+
|
|
222
|
+
const getFirst = numbers.pipe(
|
|
223
|
+
Stream.runHead
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
Effect.runPromise(getFirst).then((option) => {
|
|
227
|
+
if (Option.isSome(option)) {
|
|
228
|
+
console.log(`First: ${option.value}`) // First: 1
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
// ============================================
|
|
233
|
+
// runLast - Get last value only
|
|
234
|
+
// ============================================
|
|
235
|
+
|
|
236
|
+
const getLast = numbers.pipe(
|
|
237
|
+
Stream.runLast
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
Effect.runPromise(getLast).then((option) => {
|
|
241
|
+
if (Option.isSome(option)) {
|
|
242
|
+
console.log(`Last: ${option.value}`) // Last: 5
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
// ============================================
|
|
247
|
+
// runFold - Accumulate into single result
|
|
248
|
+
// ============================================
|
|
249
|
+
|
|
250
|
+
const sum = numbers.pipe(
|
|
251
|
+
Stream.runFold(0, (acc, n) => acc + n)
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
Effect.runPromise(sum).then((total) => {
|
|
255
|
+
console.log(`Sum: ${total}`) // Sum: 15
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// ============================================
|
|
259
|
+
// runCount - Count elements
|
|
260
|
+
// ============================================
|
|
261
|
+
|
|
262
|
+
const count = numbers.pipe(Stream.runCount)
|
|
263
|
+
|
|
264
|
+
Effect.runPromise(count).then((n) => {
|
|
265
|
+
console.log(`Count: ${n}`) // Count: 5
|
|
266
|
+
})
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Rationale:**
|
|
270
|
+
|
|
271
|
+
Streams are lazy - nothing happens until you run them. Choose your run method based on what you need: all results, per-item effects, or just completion.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
Effect provides several ways to consume a stream, each optimized for different use cases:
|
|
277
|
+
|
|
278
|
+
| Method | Returns | Use When |
|
|
279
|
+
|--------|---------|----------|
|
|
280
|
+
| **runCollect** | `Chunk<A>` | Need all results in memory |
|
|
281
|
+
| **runForEach** | `void` | Process each item for side effects |
|
|
282
|
+
| **runDrain** | `void` | Run for side effects, ignore values |
|
|
283
|
+
| **runHead** | `Option<A>` | Only need first value |
|
|
284
|
+
| **runLast** | `Option<A>` | Only need last value |
|
|
285
|
+
| **runFold** | `S` | Accumulate into single result |
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### Take and Drop Stream Elements
|
|
292
|
+
|
|
293
|
+
**Rule:** Use take/drop to control stream size, takeWhile/dropWhile for conditional limits.
|
|
294
|
+
|
|
295
|
+
**Good Example:**
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { Effect, Stream } from "effect"
|
|
299
|
+
|
|
300
|
+
const numbers = Stream.make(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
|
301
|
+
|
|
302
|
+
// ============================================
|
|
303
|
+
// take - Get first N elements
|
|
304
|
+
// ============================================
|
|
305
|
+
|
|
306
|
+
const firstThree = numbers.pipe(
|
|
307
|
+
Stream.take(3),
|
|
308
|
+
Stream.runCollect
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
Effect.runPromise(firstThree).then((chunk) => {
|
|
312
|
+
console.log([...chunk]) // [1, 2, 3]
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
// ============================================
|
|
316
|
+
// drop - Skip first N elements
|
|
317
|
+
// ============================================
|
|
318
|
+
|
|
319
|
+
const skipThree = numbers.pipe(
|
|
320
|
+
Stream.drop(3),
|
|
321
|
+
Stream.runCollect
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
Effect.runPromise(skipThree).then((chunk) => {
|
|
325
|
+
console.log([...chunk]) // [4, 5, 6, 7, 8, 9, 10]
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
// ============================================
|
|
329
|
+
// Combine for pagination (skip + limit)
|
|
330
|
+
// ============================================
|
|
331
|
+
|
|
332
|
+
const page2 = numbers.pipe(
|
|
333
|
+
Stream.drop(3), // Skip first page
|
|
334
|
+
Stream.take(3), // Take second page
|
|
335
|
+
Stream.runCollect
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
Effect.runPromise(page2).then((chunk) => {
|
|
339
|
+
console.log([...chunk]) // [4, 5, 6]
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
// ============================================
|
|
343
|
+
// takeWhile - Take while condition is true
|
|
344
|
+
// ============================================
|
|
345
|
+
|
|
346
|
+
const untilFive = numbers.pipe(
|
|
347
|
+
Stream.takeWhile((n) => n < 5),
|
|
348
|
+
Stream.runCollect
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
Effect.runPromise(untilFive).then((chunk) => {
|
|
352
|
+
console.log([...chunk]) // [1, 2, 3, 4]
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// ============================================
|
|
356
|
+
// dropWhile - Skip while condition is true
|
|
357
|
+
// ============================================
|
|
358
|
+
|
|
359
|
+
const afterFive = numbers.pipe(
|
|
360
|
+
Stream.dropWhile((n) => n < 5),
|
|
361
|
+
Stream.runCollect
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
Effect.runPromise(afterFive).then((chunk) => {
|
|
365
|
+
console.log([...chunk]) // [5, 6, 7, 8, 9, 10]
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// ============================================
|
|
369
|
+
// takeUntil - Take until condition becomes true
|
|
370
|
+
// ============================================
|
|
371
|
+
|
|
372
|
+
const untilSix = numbers.pipe(
|
|
373
|
+
Stream.takeUntil((n) => n === 6),
|
|
374
|
+
Stream.runCollect
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
Effect.runPromise(untilSix).then((chunk) => {
|
|
378
|
+
console.log([...chunk]) // [1, 2, 3, 4, 5, 6]
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
// ============================================
|
|
382
|
+
// Practical: Process file with header
|
|
383
|
+
// ============================================
|
|
384
|
+
|
|
385
|
+
const fileLines = Stream.make(
|
|
386
|
+
"# Header",
|
|
387
|
+
"# Comment",
|
|
388
|
+
"data1",
|
|
389
|
+
"data2",
|
|
390
|
+
"data3"
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
const dataOnly = fileLines.pipe(
|
|
394
|
+
Stream.dropWhile((line) => line.startsWith("#")),
|
|
395
|
+
Stream.runCollect
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
Effect.runPromise(dataOnly).then((chunk) => {
|
|
399
|
+
console.log([...chunk]) // ["data1", "data2", "data3"]
|
|
400
|
+
})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Rationale:**
|
|
404
|
+
|
|
405
|
+
Use `take` to limit how many elements to process. Use `drop` to skip elements. Add `While` variants for condition-based limits.
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
Streams can be infinite or very large. These operators let you:
|
|
411
|
+
|
|
412
|
+
1. **Limit processing** - Only take what you need
|
|
413
|
+
2. **Skip headers** - Drop first N elements
|
|
414
|
+
3. **Conditional limits** - Take/drop based on predicates
|
|
415
|
+
4. **Pagination** - Implement skip/limit patterns
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
|