Skip to content

Commit

Permalink
Update Go 1.23 docs and readme (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
destel authored Aug 18, 2024
1 parent bb2b356 commit c35816e
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 34 deletions.
49 changes: 25 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ Rill has got you covered.


## Motivation
Rill is not an iterator library or a functional programming library, though it may look like one.
It's a concurrency library built specifically for Go channels.
Rill might look like an iterator or functional programming library, but
at its core it's a concurrency library made specifically for Go channels.

There is a consensus in the Go community that functional programming style operations like Map, Filter, ForEach and others are
not idiomatic in Go, and that basic for-loops are better, faster, and more concise. This is true for slices,
Expand All @@ -37,7 +37,7 @@ but for channels, the complexity can quickly escalate beyond a basic for-loop as
- For a multi-stage pipeline, everything must be manually managed at each stage, causing complexity to grow non-linearly
- Features like batching or ordered fan-in require even more complex orchestration and synchronization

The list can be continued. And while tools like channels, ErrGroups or semaphores are powerful on their own,
These increasing levels of complexity introduce several challenges. While tools like channels, ErrGroups or semaphores are powerful on their own,
combining them into a more complex logic, can lead to code with lots of boilerplate that's difficult to write, read, and maintain.

Rill was born out of the desire to remove code duplication and to encapsulate all this complexity in a library with a
Expand All @@ -46,7 +46,6 @@ functional-style operations on channels, providing a natural way to achieve this




## Example Usage
Consider an example application that loads users from an API concurrently,
updates their status to active and saves them back,
Expand Down Expand Up @@ -233,7 +232,7 @@ Rill provides several blocking functions out of the box:
[Example](https://pkg.go.dev/github.com/destel/rill#example-ForEach)
- **ToSlice:** Collects all stream items into a slice.
[Example](https://pkg.go.dev/github.com/destel/rill#example-ToSlice)
- **ToSeq2:**: Convert the stream items to a value-errors paris iterator.
- **ToSeq2:** Converts a stream into an iterator of value-error pairs.
[Example](https://pkg.go.dev/github.com/destel/rill#example-ToSeq2)
- **Reduce:** Concurrently reduces the stream to a single value, using a user provided reducer function.
[Example](https://pkg.go.dev/github.com/destel/rill#example-Reduce)
Expand Down Expand Up @@ -357,15 +356,30 @@ func main() {
}
```

## 1.23 iterator integration
Starting from Golang 1.23, the language supports the "for-range" function. This
allows users to directly use a for loop on an iterator in the iter package.
## Go 1.23 Iterators
Starting from Go 1.23, the language supports *range over function*, allowing users to define custom iterators
for use in for-range loops. This feature enables Rill to integrate seamlessly with existing iterator-based functions
in the standard library and third-party packages.

Rill provides **FromSeq** and **FromSeq2** functions to convert an iterator into
a stream. Additionally, there's a **ToSeq2** function to convert a stream back into an iterator.

**ToSeq2** can be a good alternative to **ForEach** when concurrency is not needed.
It gives more control and performs all necessary cleanup and draining, even if the loop is terminated early using *break* or *return*.

The Rill library supports using FromSeq2 or FromSeq to convert an iterator into
a stream. It also supports using ToSeq2 to convert a stream into value-error
pairs iterator.
[Full runnable example](https://pkg.go.dev/github.com/destel/rill#example-ToSeq2)

```go
func main() {
nums := rill.FromSeq2(genPositive(40))
squares := rill.Map(nums, 4, func(x int) (int, error) {
return x * x, nil
})
for val, err := range rill.ToSeq2(squares) {
fmt.Println(val, err)
}
}

func genPositive(to int) iter.Seq2[int, error] {
return func(yield func(i int, err error) bool) {
for i := 1; i <= to; i++ {
Expand All @@ -375,19 +389,6 @@ func genPositive(to int) iter.Seq2[int, error] {
}
}
}

func main() {
nums := rill.FromSeq2(genPositive(40))
doubleNums := rill.Map(nums, 4, func(x int) (int, error) {
return x * x, nil
})
for val, err := range rill.ToSeq2(doubleNums) {
fmt.Println(val, err)
// Just want to print the first result and discard the other values.
// Don't worry about goroutine leak here. rill handle it for you.
break
}
}
```


Expand Down
1 change: 0 additions & 1 deletion example123_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build go1.23
// +build go1.23

package rill_test

Expand Down
17 changes: 9 additions & 8 deletions iter.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//go:build go1.23
// +build go1.23

package rill

import (
"iter"
)

// FromSeq converts a iterator into a stream.
// FromSeq converts an iterator into a stream.
// If err is not nil function returns a stream with a single error.
//
// Such function signature allows concise wrapping of functions that return an
Expand Down Expand Up @@ -36,7 +35,7 @@ func FromSeq[A any](seq iter.Seq[A], err error) <-chan Try[A] {
return out
}

// FromSeq2 converts a value-error pairs sequence into a stream.
// FromSeq2 converts an iterator of value-error pairs into a stream.
func FromSeq2[A any](seq iter.Seq2[A, error]) <-chan Try[A] {
if seq == nil {
return nil
Expand All @@ -52,12 +51,14 @@ func FromSeq2[A any](seq iter.Seq2[A, error]) <-chan Try[A] {
return out
}

// ToSeq2 converts an input stream into a sequence of value-error paris.
// ToSeq2 converts an input stream into an iterator of value-error pairs.
//
// This is a blocking ordered function that processes items sequentially. For
// error handling, ToSeq2 is different from ToSlice; it does not simply return
// the first encountered error. Instead, ToIterSeq will iterate all value-error
// paris, allowing the client to decide when to stop.
// This is a blocking ordered function that processes items sequentially.
// It does not return on the first encountered error. Instead, it iterates over all value-error
// pairs, either until the input stream is fully consumed or the loop is broken by the caller.
// So all error handling, if needed, should be done inside the iterator (for-range loop body).
//
// See the package documentation for more information on blocking ordered functions.
func ToSeq2[A any](in <-chan Try[A]) iter.Seq2[A, error] {
return func(yield func(A, error) bool) {
defer DrainNB(in)
Expand Down
1 change: 0 additions & 1 deletion iter_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build go1.23
// +build go1.23

package rill

Expand Down

0 comments on commit c35816e

Please sign in to comment.