1# Strom with Do-Notation: Visual Examples
3## Side-by-Side Comparisons
5### Example 1: Simple Transformation
13 # Strom.map (\y -> x * y)
25**Output**: `[1, 2, 3, 2, 4, 6, 3, 6, 9, ..., 30]`
29### Example 2: Filtering
35 # Strom.filter (\x -> x `mod` 2 == 0)
40import Yoga.Om.Strom.Do (guard)
44 guard (x `mod` 2 == 0)
48**Output**: `[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]`
52### Example 3: Nested Streams
57 Strom.fromArray [1, 2, 3]
59 Strom.fromArray [10, 20]
61 Strom.fromArray [100, 200]
62 # Strom.map (\z -> x + y + z)
70 x <- Strom.fromArray [1, 2, 3]
71 y <- Strom.fromArray [10, 20]
72 z <- Strom.fromArray [100, 200]
76**Output**: `[111, 211, 121, 221, 112, 212, 122, 222, 113, 213, 123, 223]`
82### Example 4: Pythagorean Triples
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)
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)]
101**Compare to Scala ZIO:**
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
113### Example 5: Paginated API
116import Yoga.Om.Strom.Do (guard)
118fetchAllUsers :: Strom Context () User
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
128 users <- fetchAllUsers
133**Without Do** (much harder to read):
136 Strom.iterate (_ + 1) 0
138 Strom.fromOm (fetchPage pageNum)
140 if Array.null page.users
142 else Strom.fromArray page.users
149### Example 6: Data Processing Pipeline
152import Yoga.Om.Strom.Do (guard, when)
154processDepartments :: Config -> Strom Context () Report
155processDepartments config = do
156 -- Get all departments
160 employee <- Strom.fromArray dept.employees
161 guard employee.active
163 -- Optional enrichment
164 enriched <- when config.enrichData do
165 Strom.fromOm (enrichEmployee employee)
168 project <- Strom.fromArray enriched.projects
169 guard (project.status == "active")
174 empName: enriched.name,
175 projectName: project.name,
176 budget: project.budget
182### Example 7: Combinations with Constraints
185import Yoga.Om.Strom.Do (guard)
187-- Find all ways to make change for $1 using coins
188makeChange :: Strom ctx err CoinCombination
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
198 pure { quarters, dimes, nickels, pennies }
202 ways <- makeChange # Strom.runCollect
203 Console.log $ "Found " <> show (Array.length ways) <> " ways to make change"
208### Example 8: Prime Numbers
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
218 n <- Strom.range 2 (upTo + 1)
224 primeList <- primes 100 # Strom.runCollect
225 -- [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, ...]
230### Example 9: Graph Traversal
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
241 depth <- Strom.range 1 (maxDepth + 1)
243 -- Build path recursively
244 path <- buildPath start depth
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)
259### Example 10: Matrix Operations
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
271-- Get diagonal elements
272diagonalIndices :: Int -> Strom ctx err (Tuple Int Int)
273diagonalIndices size = do
274 i <- Strom.range 0 size
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
288### Example 11: Nested Filtering
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
300 d <- Strom.range 1 (x + 1)
301 guard (x `mod` d == 0) -- Is divisor
302 guard (d `mod` 2 /= 0) -- Odd divisor
305 divisors <- Strom.fromArray [divisor] -- Collect all odd divisors
306 pure (Tuple x divisors)
311### Example 12: Event Stream Processing
314import Yoga.Om.Strom.Do (guard)
316type Event = { userId :: String, action :: String, timestamp :: Int }
318processEvents :: Strom Context () ProcessedEvent
324 guard (event.action /= "spam")
327 user <- Strom.fromOm (fetchUser event.userId)
330 -- Enrich with context
331 enriched <- Strom.fromOm (enrichEvent event user)
334 guard enriched.isValid
341## Complex Pipeline Comparison
343### Without Do (Nested Hell 😱)
348 # Strom.filter (\x -> x `mod` 2 == 0)
350 Strom.fromOm (fetchData x)
353 then Strom.fromArray data.items
355 Strom.fromOm (processItem item)
356 # Strom.map (\result -> {x, data, item, result})
363### With Do (Clean & Readable ✨)
366import Yoga.Om.Strom.Do (guard)
369 x <- Strom.range 1 100
370 guard (x `mod` 2 == 0)
372 data <- Strom.fromOm (fetchData x)
375 item <- Strom.fromArray data.items
376 result <- Strom.fromOm (processItem item)
378 pure {x, data, item, result}
385## Pattern: Cartesian Products
395 # Strom.map (\y -> Point {x, y})
400 x <- Strom.range 0 width
401 y <- Strom.range 0 height
410 x <- Strom.range 0 width
411 y <- Strom.range 0 height
412 z <- Strom.range 0 depth
413 pure (Point3D {x, y, z})
416### Filtered Grid (only certain cells)
419import Yoga.Om.Strom.Do (guard)
424 guard ((x + y) `mod` 2 == 0) -- Checkerboard pattern
432### Before Do-Notation
434❌ Nested bind (>>=) calls
443✅ Linear, top-to-bottom flow
448✅ Looks like imperative code
449✅ Same as Scala/Haskell
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!
471import Yoga.Om.Strom.Do as Strom
477 Strom.guard (condition x y)
483 Strom.when config.enabled do
490 inner <- innerStream outer
491 pure (combine outer inner)
496**Strom + Do-Notation = Beautiful Stream Processing!** 🌊✨
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list