Skip to content

Commit

Permalink
Add examples for slices (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
destel authored May 29, 2024
1 parent 86f62df commit 5ac6622
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 2 deletions.
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,28 @@ go get github.com/destel/rill


## Example usage
Consider an application that fetches keys from multiple URLs, retrieves their values from a key-value database in batches, and prints them.
A basic example demonstrating how **ForEach** can be used to process a list of items concurrently and handle errors.

[Full runnable example](https://pkg.go.dev/github.com/destel/rill#example-package-Basic)

```go
func main() {
items := rill.FromSlice([]string{"item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"}, nil)

err := rill.ForEach(items, 3, func(item string) error {
randomSleep(1000 * time.Millisecond) // simulate some additional work
res := strings.ToUpper(item)
fmt.Println(res)
return nil
})
if err != nil {
fmt.Println("Error:", err)
}
}
```


Consider a more advanced example: an application that fetches keys from multiple URLs, retrieves their values from a key-value database in batches, and prints them.
This example demonstrates the library's strengths in handling concurrent tasks, error propagation, batching and data streaming,
all while maintaining simplicity and efficiency.

Expand Down Expand Up @@ -327,5 +348,46 @@ func main() {
func getTemperature(city string, date time.Time) (float64, error) {
// ...
}
```


## Working with slices
Rill is designed for channel based workflows, but it can also be used with slices, thanks to its ability
to do ordered processing. Example below demonstrates how to create a **mapSLice** generic helper function that
does parallel slice processing. That helper is then used to fetch users from an API concurrently.

[Full runnable example](https://pkg.go.dev/github.com/destel/rill#example-package-Slices)

```go
type User struct {
ID int
Username string
}

// mapSLice is a helper function that does a parallel map operation on a slice of items
func mapSLice[A, B any](in []A, n int, f func(A) (B, error)) ([]B, error) {
inChan := rill.FromSlice(in, nil)
outChan := rill.OrderedMap(inChan, n, f)
return rill.ToSlice(outChan)
}

func main() {
ids := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

users, err := mapSLice(ids, 3, getUser)
if err != nil {
fmt.Println("Error:", err)
return
}

fmt.Printf("%+v\n", users)
}



// getUser fetches a user from an API
func getUser(id int) (User, error) {
// ...
}

```
44 changes: 43 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ type Measurement struct {
Temp float64
}

// A basic demonstrating how [ForEach] can be used to process a list of items concurrently.
type User struct {
ID int
Username string
}

// A basic example demonstrating how [ForEach] can be used to process a list of items concurrently.
func Example_basic() {
items := rill.FromSlice([]string{"item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"}, nil)

Expand All @@ -36,6 +41,31 @@ func Example_basic() {
}
}

// Rill is designed for channel based workflows, but it can also be used with slices, thanks to its ability
// to do ordered processing. Example below demonstrates how you can create a **mapSLice** generic helper function that
// does parallel slice processing. That helper is then used to fetch users from an API concurrently.
func Example_slices() {
startedAt := time.Now()
defer func() { fmt.Println("Elapsed:", time.Since(startedAt)) }()

ids := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

users, err := mapSLice(ids, 3, getUser)
if err != nil {
fmt.Println("Error:", err)
return
}

fmt.Printf("%+v\n", users)
}

// mapSLice is a helper function that does a parallel map operation on a slice of items
func mapSLice[A, B any](in []A, n int, f func(A) (B, error)) ([]B, error) {
inChan := rill.FromSlice(in, nil)
outChan := rill.OrderedMap(inChan, n, f)
return rill.ToSlice(outChan)
}

// This example fetches keys from a list of URLs, retrieves their values from a key-value database, and prints them.
// The pipeline leverages concurrency for fetching and processing and uses batching to reduce the number of database calls.
func Example_batching() {
Expand Down Expand Up @@ -211,3 +241,15 @@ func getTemperature(city string, date time.Time) (float64, error) {
func randomSleep(max time.Duration) {
time.Sleep(time.Duration(rand.Intn(int(max))))
}

// getUser simulates fetching a user from an API, introducing a randomized delay to simulate network latency.
func getUser(id int) (User, error) {
randomSleep(1000 * time.Millisecond) // Simulate a network delay

// generate random name adj+noun
adj := []string{"big", "small", "fast", "slow", "smart", "happy", "sad", "funny", "serious"}
noun := []string{"dog", "cat", "bird", "fish", "mouse", "elephant", "lion", "tiger", "bear", "wolf"}
username := fmt.Sprintf("%s_%s", adj[rand.Intn(len(adj))], noun[rand.Intn(len(noun))])

return User{ID: id, Username: username}, nil
}

0 comments on commit 5ac6622

Please sign in to comment.