@@ -67,7 +67,7 @@ func Example_batching() {
6767 }, nil )
6868
6969 // Group IDs into batches of 5
70- idBatches := rill .Batch (ids , 5 , 1 * time . Second )
70+ idBatches := rill .Batch (ids , 5 , - 1 )
7171
7272 // Bulk fetch users from the API
7373 // Concurrency = 3
@@ -108,7 +108,7 @@ func Example_batching() {
108108// emits partial batches, ensuring that updates are delayed by at most 100ms.
109109//
110110// For simplicity, this example does not have retries, error handling and synchronization
111- func Example_batchingWithTimeout () {
111+ func Example_batchingRealTime () {
112112 // Start the background worker that processes the updates
113113 go updateUserTimestampWorker ()
114114
@@ -170,20 +170,13 @@ func Example_ordering() {
170170 // The string to search for in the downloaded files
171171 needle := []byte ("26" )
172172
173- // Manually generate a stream of URLs from http://example.com/file-0.txt to http://example.com/file-999.txt
174- urls := make (chan rill.Try [string ])
175- go func () {
176- defer close (urls )
177- for i := 0 ; i < 1000 ; i ++ {
178- // Stop generating URLs after the context is canceled (when the file is found)
179- // This can be rewritten as a select statement, but it's not necessary
180- if err := ctx .Err (); err != nil {
181- return
182- }
173+ // Start with a stream of numbers from 0 to 999
174+ fileIDs := streamNumbers (ctx , 0 , 1000 )
183175
184- urls <- rill .Wrap (fmt .Sprintf ("https://example.com/file-%d.txt" , i ), nil )
185- }
186- }()
176+ // Generate a stream of URLs from http://example.com/file-0.txt to http://example.com/file-999.txt
177+ urls := rill .OrderedMap (fileIDs , 1 , func (id int ) (string , error ) {
178+ return fmt .Sprintf ("https://example.com/file-%d.txt" , id ), nil
179+ })
187180
188181 // Download and process the files
189182 // At most 5 files are downloaded and held in memory at the same time
@@ -251,17 +244,16 @@ func sendMessage(message string, server string) error {
251244 return nil
252245}
253246
254- // This example demonstrates using [FlatMap] to accelerate paginated API calls. Instead of fetching all users sequentially,
255- // page-by-page (which would take a long time since the API is slow and the number of pages is large), it fetches users from
256- // multiple departments in parallel. The example also shows how to write a reusable streaming wrapper around an existing
257- // API function that can be used on its own or as part of a larger pipeline.
258- func Example_parallelStreams () {
247+ // This example demonstrates using [FlatMap] to fetch users from multiple departments concurrently.
248+ // Additionally, it demonstrates how to write a reusable streaming wrapper over paginated API calls - the StreamUsers function
249+ func Example_flatMap () {
259250 ctx := context .Background ()
260251
261- // Convert a list of all departments into a stream
262- departments := rill .FromSlice (mockapi . GetDepartments () )
252+ // Start with a stream of department names
253+ departments := rill .FromSlice ([] string { "IT" , "Finance" , "Marketing" , "Support" , "Engineering" }, nil )
263254
264- // Use FlatMap to stream users from 3 departments concurrently.
255+ // Stream users from all departments concurrently.
256+ // At most 3 departments at the same time.
265257 users := rill .FlatMap (departments , 3 , func (department string ) <- chan rill.Try [* mockapi.User ] {
266258 return StreamUsers (ctx , & mockapi.UserQuery {Department : department })
267259 })
@@ -276,7 +268,7 @@ func Example_parallelStreams() {
276268
277269// StreamUsers is a reusable streaming wrapper around the mockapi.ListUsers function.
278270// It iterates through all listing pages and returns a stream of users.
279- // This function is useful on its own or as a building block for more complex pipelines.
271+ // This function is useful both on its own and as part of larger pipelines.
280272func StreamUsers (ctx context.Context , query * mockapi.UserQuery ) <- chan rill.Try [* mockapi.User ] {
281273 res := make (chan rill.Try [* mockapi.User ])
282274
@@ -309,43 +301,38 @@ func StreamUsers(ctx context.Context, query *mockapi.UserQuery) <-chan rill.Try[
309301 return res
310302}
311303
312- // This example demonstrates how to use a context for pipeline termination.
313- // The FindFirstPrime function uses several concurrent workers to find the first prime number after a given number.
314- // Internally it creates a pipeline that starts from an infinite stream of numbers. When the first prime number is found
315- // in that stream, the context gets canceled, and the pipeline terminates gracefully.
304+ // This example demonstrates how to gracefully stop a pipeline on the first error.
305+ // The CheckAllUsersExist uses several concurrent workers and returns an error as soon as it encounters a non-existent user.
306+ // Such early return triggers the context cancellation, which in turn stops all remaining users fetches.
316307func Example_context () {
317- p := FindFirstPrime (10000 , 3 ) // Use 3 concurrent workers
318- fmt .Println ("The first prime after 10000 is" , p )
308+ ctx := context .Background ()
309+
310+ // ID 999 doesn't exist, so fetching will stop after hitting it.
311+ err := CheckAllUsersExist (ctx , 3 , []int {1 , 2 , 3 , 4 , 5 , 999 , 7 , 8 , 9 , 10 })
312+ fmt .Printf ("Check result: %v\n " , err )
319313}
320314
321- // FindFirstPrime finds the first prime number after the given number, using several concurrent workers.
322- func FindFirstPrime (after int , concurrency int ) int {
323- ctx , cancel := context .WithCancel (context .Background ())
315+ // CheckAllUsersExist uses several concurrent workers to checks if all users with given IDs exist.
316+ func CheckAllUsersExist (ctx context.Context , concurrency int , ids []int ) error {
317+ // Create new context that will be canceled when this function returns
318+ ctx , cancel := context .WithCancel (ctx )
324319 defer cancel ()
325320
326- // Generate an infinite stream of numbers starting from the given number
327- numbers := make (chan rill.Try [int ])
328- go func () {
329- defer close (numbers )
330- for i := after + 1 ; ; i ++ {
331- select {
332- case <- ctx .Done ():
333- return // Stop generating numbers when the context is canceled
334- case numbers <- rill .Wrap (i , nil ):
335- }
321+ idsStream := rill .FromSlice (ids , nil )
322+
323+ // Fetch users concurrently.
324+ users := rill .Map (idsStream , concurrency , func (id int ) (* mockapi.User , error ) {
325+ u , err := mockapi .GetUser (ctx , id )
326+ if err != nil {
327+ return nil , fmt .Errorf ("failed to fetch user %d: %w" , id , err )
336328 }
337- }()
338329
339- // Filter out non-prime numbers, preserve the order
340- primes := rill .OrderedFilter (numbers , concurrency , func (x int ) (bool , error ) {
341- fmt .Println ("Checking" , x )
342- return isPrime (x ), nil
330+ fmt .Printf ("Fetched user %d\n " , id )
331+ return u , nil
343332 })
344333
345- // Get the first prime and cancel the context
346- // This stops number generation and allows goroutines to exit
347- result , _ , _ := rill .First (primes )
348- return result
334+ // Return the first error (if any) and cancel remaining fetches via context
335+ return rill .Err (users )
349336}
350337
351338// --- Function examples ---
@@ -591,10 +578,10 @@ func ExampleForEach() {
591578 // Convert a slice of numbers into a stream
592579 numbers := rill .FromSlice ([]int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }, nil )
593580
594- // Do something with each number and print the result
581+ // Square each number and print the result
595582 // Concurrency = 3
596583 err := rill .ForEach (numbers , 3 , func (x int ) error {
597- y := doSomethingWithNumber (x )
584+ y := square (x )
598585 fmt .Println (y )
599586 return nil
600587 })
@@ -610,15 +597,15 @@ func ExampleForEach_ordered() {
610597 // Convert a slice of numbers into a stream
611598 numbers := rill .FromSlice ([]int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }, nil )
612599
613- // Do something with each number
600+ // Square each number
614601 // Concurrency = 3; Ordered
615- results := rill .OrderedMap (numbers , 3 , func (x int ) (int , error ) {
616- return doSomethingWithNumber (x ), nil
602+ squares := rill .OrderedMap (numbers , 3 , func (x int ) (int , error ) {
603+ return square (x ), nil
617604 })
618605
619606 // Print results.
620607 // Concurrency = 1; Ordered
621- err := rill .ForEach (results , 1 , func (y int ) error {
608+ err := rill .ForEach (squares , 1 , func (y int ) error {
622609 fmt .Println (y )
623610 return nil
624611 })
@@ -633,11 +620,11 @@ func ExampleMap() {
633620
634621 // Transform each number
635622 // Concurrency = 3
636- results := rill .Map (numbers , 3 , func (x int ) (int , error ) {
637- return doSomethingWithNumber (x ), nil
623+ squares := rill .Map (numbers , 3 , func (x int ) (int , error ) {
624+ return square (x ), nil
638625 })
639626
640- printStream (results )
627+ printStream (squares )
641628}
642629
643630// The same example as for the [Map], but using ordered versions of functions.
@@ -647,11 +634,11 @@ func ExampleOrderedMap() {
647634
648635 // Transform each number
649636 // Concurrency = 3; Ordered
650- results := rill .OrderedMap (numbers , 3 , func (x int ) (int , error ) {
651- return doSomethingWithNumber (x ), nil
637+ squares := rill .OrderedMap (numbers , 3 , func (x int ) (int , error ) {
638+ return square (x ), nil
652639 })
653640
654- printStream (results )
641+ printStream (squares )
655642}
656643
657644func ExampleMapReduce () {
@@ -709,11 +696,11 @@ func ExampleToSlice() {
709696
710697 // Transform each number
711698 // Concurrency = 3; Ordered
712- results := rill .OrderedMap (numbers , 3 , func (x int ) (int , error ) {
713- return doSomethingWithNumber (x ), nil
699+ squares := rill .OrderedMap (numbers , 3 , func (x int ) (int , error ) {
700+ return square (x ), nil
714701 })
715702
716- resultsSlice , err := rill .ToSlice (results )
703+ resultsSlice , err := rill .ToSlice (squares )
717704
718705 fmt .Println ("Result:" , resultsSlice )
719706 fmt .Println ("Error:" , err )
@@ -735,15 +722,8 @@ func ExampleUnbatch() {
735722
736723// --- Helpers ---
737724
738- // helper function that squares the number
725+ // helper function that checks if a number is prime
739726// and simulates some additional work using sleep
740- func doSomethingWithNumber (x int ) int {
741- randomSleep (500 * time .Millisecond ) // simulate some additional work
742- return x * x
743- }
744-
745- // naive prime number check.
746- // also simulates some additional work using sleep
747727func isPrime (n int ) bool {
748728 randomSleep (500 * time .Millisecond ) // simulate some additional work
749729
@@ -758,6 +738,29 @@ func isPrime(n int) bool {
758738 return true
759739}
760740
741+ // helper function that squares the number
742+ // and simulates some additional work using sleep
743+ func square (x int ) int {
744+ randomSleep (500 * time .Millisecond ) // simulate some additional work
745+ return x * x
746+ }
747+
748+ // helper function that creates a stream of numbers [start, end) and respects the context
749+ func streamNumbers (ctx context.Context , start , end int ) <- chan rill.Try [int ] {
750+ out := make (chan rill.Try [int ])
751+ go func () {
752+ defer close (out )
753+ for i := start ; i < end ; i ++ {
754+ select {
755+ case <- ctx .Done ():
756+ return
757+ case out <- rill.Try [int ]{Value : i }:
758+ }
759+ }
760+ }()
761+ return out
762+ }
763+
761764// printStream prints all items from a stream (one per line) and an error if any.
762765func printStream [A any ](stream <- chan rill.Try [A ]) {
763766 fmt .Println ("Result:" )
0 commit comments