git.iko.soy/rowtype-yoga/purescript-yoga-om-strom
on v0.1.1 help sign in
496 lines 9.9 kB history raw
1# Strom with Do-Notation: Visual Examples
3## Side-by-Side Comparisons
5### Example 1: Simple Transformation
7#### Without Do
8```purescript
9result =
10 Strom.range 1 10
11 >>= (\x ->
12 Strom.range 1 3
13 # Strom.map (\y -> x * y)
14 )
15```
17#### With Do ✨
18```purescript
19result = do
20 x <- Strom.range 1 10
21 y <- Strom.range 1 3
22 pure (x * y)
23```
25**Output**: `[1, 2, 3, 2, 4, 6, 3, 6, 9, ..., 30]`
27---
29### Example 2: Filtering
31#### Without Do
32```purescript
33evens =
34 Strom.range 1 20
35 # Strom.filter (\x -> x `mod` 2 == 0)
36```
38#### With Do ✨
39```purescript
40import Yoga.Om.Strom.Do (guard)
42evens = do
43 x <- Strom.range 1 20
44 guard (x `mod` 2 == 0)
45 pure x
46```
48**Output**: `[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]`
50---
52### Example 3: Nested Streams
54#### Without Do
55```purescript
56result =
57 Strom.fromArray [1, 2, 3]
58 >>= (\x ->
59 Strom.fromArray [10, 20]
60 >>= (\y ->
61 Strom.fromArray [100, 200]
62 # Strom.map (\z -> x + y + z)
63 )
64 )
65```
67#### With Do ✨
68```purescript
69result = do
70 x <- Strom.fromArray [1, 2, 3]
71 y <- Strom.fromArray [10, 20]
72 z <- Strom.fromArray [100, 200]
73 pure (x + y + z)
74```
76**Output**: `[111, 211, 121, 221, 112, 212, 122, 222, 113, 213, 123, 223]`
78---
80## Real-World Examples
82### Example 4: Pythagorean Triples
84```purescript
85import Yoga.Om.Strom.Do (guard)
87pythagoreanTriples :: Int -> Strom ctx err (Tuple Int Int Int)
88pythagoreanTriples maxN = do
89 a <- Strom.range 1 (maxN + 1)
90 b <- Strom.range a (maxN + 1)
91 c <- Strom.range b (maxN + 1)
92 guard (a * a + b * b == c * c)
93 pure (Tuple a b c)
95-- Usage
96main = do
97 triples <- pythagoreanTriples 20 # Strom.runCollect
98 -- triples = [(3,4,5), (5,12,13), (6,8,10), (8,15,17), (9,12,15), (12,16,20)]
99```
101**Compare to Scala ZIO:**
102```scala
103def pythagoreanTriples(maxN: Int) = for {
104 a <- ZStream.range(1, maxN + 1)
105 b <- ZStream.range(a, maxN + 1)
106 c <- ZStream.range(b, maxN + 1)
107 if a * a + b * b == c * c
108} yield (a, b, c)
109```
111---
113### Example 5: Paginated API
115```purescript
116import Yoga.Om.Strom.Do (guard)
118fetchAllUsers :: Strom Context () User
119fetchAllUsers = do
120 pageNum <- Strom.iterate (_ + 1) 0
121 page <- Strom.fromOm (fetchPage pageNum)
122 guard (not $ Array.null page.users)
123 user <- Strom.fromArray page.users
124 pure user
126-- Usage
127main = do
128 users <- fetchAllUsers
129 # Strom.take 100
130 # Strom.runCollect
131```
133**Without Do** (much harder to read):
134```purescript
135fetchAllUsers' =
136 Strom.iterate (_ + 1) 0
137 >>= (\pageNum ->
138 Strom.fromOm (fetchPage pageNum)
139 >>= (\page ->
140 if Array.null page.users
141 then Strom.empty
142 else Strom.fromArray page.users
143 )
144 )
145```
147---
149### Example 6: Data Processing Pipeline
151```purescript
152import Yoga.Om.Strom.Do (guard, when)
154processDepartments :: Config -> Strom Context () Report
155processDepartments config = do
156 -- Get all departments
157 dept <- departments
159 -- Get employees
160 employee <- Strom.fromArray dept.employees
161 guard employee.active
163 -- Optional enrichment
164 enriched <- when config.enrichData do
165 Strom.fromOm (enrichEmployee employee)
167 -- Get projects
168 project <- Strom.fromArray enriched.projects
169 guard (project.status == "active")
171 -- Build report
172 pure {
173 deptName: dept.name,
174 empName: enriched.name,
175 projectName: project.name,
176 budget: project.budget
177 }
178```
180---
182### Example 7: Combinations with Constraints
184```purescript
185import Yoga.Om.Strom.Do (guard)
187-- Find all ways to make change for $1 using coins
188makeChange :: Strom ctx err CoinCombination
189makeChange = do
190 quarters <- Strom.range 0 5 -- 0-4 quarters
191 dimes <- Strom.range 0 11 -- 0-10 dimes
192 nickels <- Strom.range 0 21 -- 0-20 nickels
193 pennies <- Strom.range 0 101 -- 0-100 pennies
195 let total = quarters * 25 + dimes * 10 + nickels * 5 + pennies
196 guard (total == 100)
198 pure { quarters, dimes, nickels, pennies }
200-- Usage
201main = do
202 ways <- makeChange # Strom.runCollect
203 Console.log $ "Found " <> show (Array.length ways) <> " ways to make change"
204```
206---
208### Example 8: Prime Numbers
210```purescript
211import Yoga.Om.Strom.Do (guard)
213isPrime :: Int -> Boolean
214isPrime n = n > 1 && all (\d -> n `mod` d /= 0) (range 2 (n - 1))
216primes :: Int -> Strom ctx err Int
217primes upTo = do
218 n <- Strom.range 2 (upTo + 1)
219 guard (isPrime n)
220 pure n
222-- Usage
223main = do
224 primeList <- primes 100 # Strom.runCollect
225 -- [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, ...]
226```
228---
230### Example 9: Graph Traversal
232```purescript
233import Yoga.Om.Strom.Do (guard)
235type Graph = Map Node (Array Node)
237-- Find all paths of length N from start node
238findPaths :: Graph -> Node -> Int -> Strom ctx err Path
239findPaths graph start maxDepth = do
240 -- Try each depth
241 depth <- Strom.range 1 (maxDepth + 1)
243 -- Build path recursively
244 path <- buildPath start depth
246 pure path
248 where
249 buildPath :: Node -> Int -> Strom ctx err Path
250 buildPath current 0 = Strom.succeed [current]
251 buildPath current n = do
252 neighbor <- Strom.fromArray (Map.lookup current graph # fromMaybe [])
253 restOfPath <- buildPath neighbor (n - 1)
254 pure (current : restOfPath)
255```
257---
259### Example 10: Matrix Operations
261```purescript
262import Yoga.Om.Strom.Do (guard)
264-- Generate all matrix indices
265matrixIndices :: Int -> Int -> Strom ctx err (Tuple Int Int)
266matrixIndices rows cols = do
267 row <- Strom.range 0 rows
268 col <- Strom.range 0 cols
269 pure (Tuple row col)
271-- Get diagonal elements
272diagonalIndices :: Int -> Strom ctx err (Tuple Int Int)
273diagonalIndices size = do
274 i <- Strom.range 0 size
275 pure (Tuple i i)
277-- Get upper triangle (excluding diagonal)
278upperTriangle :: Int -> Strom ctx err (Tuple Int Int)
279upperTriangle size = do
280 row <- Strom.range 0 size
281 col <- Strom.range 0 size
282 guard (col > row)
283 pure (Tuple row col)
284```
286---
288### Example 11: Nested Filtering
290```purescript
291import Yoga.Om.Strom.Do (guard)
293-- Find all even numbers with odd divisors
294evenWithOddDivisors :: Strom ctx err (Tuple Int (Array Int))
295evenWithOddDivisors = do
296 x <- Strom.range 2 51
297 guard (x `mod` 2 == 0) -- Even numbers only
299 divisor <- do
300 d <- Strom.range 1 (x + 1)
301 guard (x `mod` d == 0) -- Is divisor
302 guard (d `mod` 2 /= 0) -- Odd divisor
303 pure d
305 divisors <- Strom.fromArray [divisor] -- Collect all odd divisors
306 pure (Tuple x divisors)
307```
309---
311### Example 12: Event Stream Processing
313```purescript
314import Yoga.Om.Strom.Do (guard)
316type Event = { userId :: String, action :: String, timestamp :: Int }
318processEvents :: Strom Context () ProcessedEvent
319processEvents = do
320 -- Get events
321 event <- eventSource
323 -- Filter spam
324 guard (event.action /= "spam")
326 -- Get user data
327 user <- Strom.fromOm (fetchUser event.userId)
328 guard user.active
330 -- Enrich with context
331 enriched <- Strom.fromOm (enrichEvent event user)
333 -- Validate
334 guard enriched.isValid
336 pure enriched
337```
339---
341## Complex Pipeline Comparison
343### Without Do (Nested Hell 😱)
345```purescript
346complexPipeline =
347 Strom.range 1 100
348 # Strom.filter (\x -> x `mod` 2 == 0)
349 >>= (\x ->
350 Strom.fromOm (fetchData x)
351 >>= (\data ->
352 if data.isValid
353 then Strom.fromArray data.items
354 >>= (\item ->
355 Strom.fromOm (processItem item)
356 # Strom.map (\result -> {x, data, item, result})
357 )
358 else Strom.empty
359 )
360 )
361```
363### With Do (Clean & Readable ✨)
365```purescript
366import Yoga.Om.Strom.Do (guard)
368complexPipeline = do
369 x <- Strom.range 1 100
370 guard (x `mod` 2 == 0)
372 data <- Strom.fromOm (fetchData x)
373 guard data.isValid
375 item <- Strom.fromArray data.items
376 result <- Strom.fromOm (processItem item)
378 pure {x, data, item, result}
379```
381**Much cleaner!** 🎉
383---
385## Pattern: Cartesian Products
387### 2D Grid
389```purescript
390-- Without Do
391grid2D =
392 Strom.range 0 width
393 >>= (\x ->
394 Strom.range 0 height
395 # Strom.map (\y -> Point {x, y})
396 )
398-- With Do ✨
399grid2D = do
400 x <- Strom.range 0 width
401 y <- Strom.range 0 height
402 pure (Point {x, y})
403```
405### 3D Grid
407```purescript
408-- With Do ✨
409grid3D = do
410 x <- Strom.range 0 width
411 y <- Strom.range 0 height
412 z <- Strom.range 0 depth
413 pure (Point3D {x, y, z})
414```
416### Filtered Grid (only certain cells)
418```purescript
419import Yoga.Om.Strom.Do (guard)
421checkerboard = do
422 x <- Strom.range 0 8
423 y <- Strom.range 0 8
424 guard ((x + y) `mod` 2 == 0) -- Checkerboard pattern
425 pure (Square {x, y})
426```
428---
430## Comparison Summary
432### Before Do-Notation
433```purescript
434❌ Nested bind (>>=) calls
435❌ Hard to read
436❌ Lots of lambdas
437❌ Deep indentation
438❌ Hard to add filters
439```
441### With Do-Notation
442```purescript
443✅ Linear, top-to-bottom flow
444✅ Easy to read
445✅ No explicit lambdas
446✅ Flat indentation
447✅ Easy guards/filters
448✅ Looks like imperative code
449✅ Same as Scala/Haskell
450```
452---
454## Key Takeaways
4561. **Do-notation makes Strom streams look imperative** but they're still pure and lazy!
4582. **Guards are powerful** - use `guard` from `Yoga.Om.Strom.Do` for filtering
4603. **Cartesian products are natural** - just use multiple `<-` binds
4624. **Much more readable** than nested `>>=` calls
4645. **Same as Scala ZIO** - if you know Scala for-comprehensions, you know this!
466---
468## Quick Reference
470```purescript
471import Yoga.Om.Strom.Do as Strom
473-- Basic pattern
474result = do
475 x <- source1
476 y <- source2
477 Strom.guard (condition x y)
478 pure (combine x y)
480-- With when/unless
481result = do
482 item <- items
483 Strom.when config.enabled do
484 process item
485 pure item
487-- Nested streams
488result = do
489 outer <- outerStream
490 inner <- innerStream outer
491 pure (combine outer inner)
492```
494---
496**Strom + Do-Notation = Beautiful Stream Processing!** 🌊✨