1# Strom Do-Notation Guide
3Yes! **Strom fully supports do-notation** because it implements the `Monad` type class.
71. [Basic Do-Notation](#basic-do-notation)
82. [Comprehension-Style](#comprehension-style)
93. [Cartesian Products](#cartesian-products)
104. [Guards and Filtering](#guards-and-filtering)
115. [Applicative Do (ado)](#applicative-do-ado)
126. [Comparison with For-Comprehensions](#comparison-with-for-comprehensions)
16### Standard Monadic Bind
19import Yoga.Om.Strom as Strom
21-- FlatMap in do-notation
22stream :: Strom ctx err Int
29stream' = Strom.range 1 5
32 # Strom.map (\y -> x * y)
35-- Result: [1, 2, 3, 2, 4, 6, 3, 6, 9, 4, 8, 12]
38### Sequential Processing
41processData :: Strom ctx err Result
44 user <- fetchUser userId -- Om effect
45 orders <- fetchOrders user.id -- Om effect
48-- Each userId produces a stream of orders
49-- Result is flattened automatically
54### With Guards (Import from Strom.Do)
57import Yoga.Om.Strom.Do as Strom
60pythagoreanTriples :: Int -> Strom ctx err (Tuple3 Int Int Int)
61pythagoreanTriples n = do
62 a <- Strom.range 1 (n + 1)
63 b <- Strom.range a (n + 1)
64 c <- Strom.range b (n + 1)
65 Strom.guard (a * a + b * b == c * c)
68-- Result: [(3,4,5), (5,12,13), (6,8,10), (8,15,17), (9,12,15), ...]
74import Yoga.Om.Strom.Do (guard)
76evens :: Strom ctx err Int
78 x <- Strom.range 1 100
79 guard (x `mod` 2 == 0)
83evens' = Strom.range 1 100
84 # Strom.filter (\x -> x `mod` 2 == 0)
90import Yoga.Om.Strom.Do (guard)
92isPrime :: Int -> Boolean
93isPrime n = n > 1 && Array.all (\d -> n `mod` d /= 0) (Array.range 2 (n - 1))
95primes :: Int -> Strom ctx err Int
97 n <- Strom.range 2 (upTo + 1)
101-- primes 20 -> [2, 3, 5, 7, 11, 13, 17, 19]
109import Data.Tuple (Tuple(..))
111allPairs :: Strom ctx err (Tuple Int Int)
117-- Result: [(1,1), (1,2), (2,1), (2,2), (3,1), (3,2)]
120import Yoga.Om.Strom.Do (for2)
121allPairs' = for2 (Strom.range 1 4) (Strom.range 1 3) Tuple
127allTriples :: Strom ctx err (Tuple3 Int Int Int)
134-- Result: [(1,1,1), (1,1,2), (1,1,3), (1,2,1), ...]
137import Yoga.Om.Strom.Do (for3)
148import Yoga.Om.Strom.Do (guard)
150sumToN :: Int -> Strom ctx err (Tuple Int Int)
152 x <- Strom.range 1 (n + 1)
153 y <- Strom.range 1 (n + 1)
157-- sumToN 5 -> [(1,4), (2,3), (3,2), (4,1)]
160## Guards and Filtering
165import Yoga.Om.Strom.Do (guard)
167complexFilter :: Strom ctx err Int
169 x <- Strom.range 1 100
170 guard (x `mod` 2 == 0) -- Even
171 guard (x `mod` 3 == 0) -- Divisible by 3
172 guard (x < 50) -- Less than 50
175-- Result: [6, 12, 18, 24, 30, 36, 42, 48]
181import Yoga.Om.Strom.Do (when, unless)
183conditionalStream :: Boolean -> Strom ctx err Int
184conditionalStream includeEvens = do
185 x <- Strom.range 1 10
187 guard (x `mod` 2 == 0)
191## Applicative Do (ado)
193For **independent** parallel operations, use `ado` notation:
196-- Sequential (do) - second depends on first
197sequential :: Strom ctx err (Tuple User Orders)
199 user <- fetchUser 123
200 orders <- fetchOrders user.id -- Depends on user!
201 pure (Tuple user orders)
203-- Parallel (ado) - independent
204parallel :: Strom ctx err (Tuple User Profile)
206 user <- fetchUser 123
207 profile <- fetchProfile 123 -- Independent!
208 in Tuple user profile
210-- With ado, both fetch concurrently
213### Ado with Multiple Streams
216-- Zip-like behavior with ado
217combined :: Strom ctx err Result
224-- This zips the three streams together
227## Comparison with For-Comprehensions
229### Scala (ZIO ZStream)
234 x <- ZStream.range(1, 5)
235 y <- ZStream.range(1, 3)
236 if x % 2 == 0 // guard
240### PureScript (Strom)
244import Yoga.Om.Strom.Do (guard)
249 guard (x `mod` 2 == 0) -- guard
253**Nearly identical!** ๐
255## Real-World Examples
257### API Pagination with Do-Notation
260import Yoga.Om.Strom.Do (guard)
262fetchAllUsers :: Strom Context () User
264 page <- Strom.iterate (_ + 1) 0
265 pageData <- Strom.fromOm (fetchPage page)
266 guard (not $ Array.null pageData.users)
267 user <- Strom.fromArray pageData.users
271### Nested Data Processing
274processDepartments :: Strom Context () EmployeeReport
275processDepartments = do
277 employee <- Strom.fromArray dept.employees
278 guard employee.active
279 projects <- Strom.fromArray employee.projects
280 guard (projects.status == "active")
281 pure { employee: employee.name, project: projects.name }
284### Combinations with Constraints
287import Yoga.Om.Strom.Do (guard)
289-- All ways to make change for $1 using quarters, dimes, nickels
290makeChange :: Strom ctx err (Tuple3 Int Int Int)
292 quarters <- Strom.range 0 5 -- 0-4 quarters
293 dimes <- Strom.range 0 11 -- 0-10 dimes
294 nickels <- Strom.range 0 21 -- 0-20 nickels
295 let total = quarters * 25 + dimes * 10 + nickels * 5
297 pure (Tuple3 quarters dimes nickels)
303import Yoga.Om.Strom.Do (guard)
305traversePaths :: Graph -> Node -> Int -> Strom ctx err Path
306traversePaths graph start maxDepth = do
307 depth <- Strom.range 1 (maxDepth + 1)
308 guard (depth <= maxDepth)
309 node <- Strom.fromArray (graph.neighbors start)
310 path <- buildPath start node depth
316### Recursive Do-Notation
319fibonacci :: Strom ctx err Int
321 pair <- Strom.iterate (\(Tuple a b) -> Tuple b (a + b)) (Tuple 0 1)
324-- Take first 10: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
327### Nested Comprehensions
330import Yoga.Om.Strom.Do (guard)
332nestedPatterns :: Strom ctx err (Tuple Int Int)
334 x <- Strom.range 1 10
335 guard (x `mod` 2 == 0)
338 guard (n `mod` 2 /= 0)
343### Conditional Streaming
346import Yoga.Om.Strom.Do (when, unless)
348conditionalProcess :: Config -> Strom ctx err Result
349conditionalProcess config = do
351 when config.enableValidation do
352 validated <- validateItem item
353 guard validated.isValid
354 when config.enableEnrichment do
361Because Strom implements these type classes:
364instance Functor (Strom ctx err) -- map, <$>
365instance Apply (Strom ctx err) -- <*>, apply
366instance Applicative (Strom ctx err) -- pure
367instance Bind (Strom ctx err) -- >>=, bind
368instance Monad (Strom ctx err) -- do-notation
371You get all these for free:
380## Performance Considerations
382### Do-Notation = FlatMap
393 stream2 # Strom.map (\y -> x + y)
397For large Cartesian products, this can be expensive!
399### When to Use Do-Notation
401โ
**Good Use Cases:**
402- Dependent operations (next depends on previous)
403- Comprehension-style filtering
406- Small Cartesian products
409- Large Cartesian products (use `zip` instead)
410- Independent operations (use `ado` or `mapParallel`)
411- Simple transformations (use `map`)
413### Optimization Example
416-- โ Slow for large streams
418 x <- Strom.range 1 10000
419 y <- Strom.range 1 10000
424 (Strom.range 1 10000)
425 (Strom.range 1 10000)
428## Helper Module: Yoga.Om.Strom.Do
430Import the helper module for comprehension-style programming:
433import Yoga.Om.Strom.Do as Strom
436 x <- Strom.range 1 10
437 Strom.guard (x `mod` 2 == 0)
444- `guard :: Boolean -> Strom ctx err Unit` - Filter in do-notation
445- `when :: Boolean -> Strom ctx err a -> Strom ctx err a` - Conditional
446- `unless :: Boolean -> Strom ctx err a -> Strom ctx err a` - Negated conditional
447- `for2 :: Strom a -> Strom b -> (a -> b -> c) -> Strom c` - 2D comprehension
448- `for3 :: Strom a -> Strom b -> Strom c -> (a -> b -> c -> d) -> Strom d` - 3D comprehension
449- `replicateEach :: Int -> a -> Strom a` - Replicate element
450- `comprehension :: Strom a -> Strom a` - Identity (for clarity)
451- `generate :: Strom a -> Strom a` - Identity (for clarity)
455### โ
Yes, Strom Has Full Do-Notation Support!
457Because Strom implements `Monad`, you get:
4591. **Regular do-notation** - For dependent operations
4602. **Applicative do (ado)** - For independent parallel operations
4613. **Comprehension-style** - With guards and filtering
4624. **Cartesian products** - Natural in do-notation
4635. **All monadic combinators** - `>>=`, `>>`, `<$>`, `<*>`, etc.
465### Compare: Scala vs PureScript
470 x <- ZStream.range(1, 10)
471 y <- ZStream.range(1, x)
478import Yoga.Om.Strom.Do (guard)
481 x <- Strom.range 1 10
483 guard (x `mod` 2 == 0)
487**Nearly identical!** ๐
491**Strom + Do-Notation = Powerful Stream Comprehensions!** ๐
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