Reference · Collections
Sequence & Traverse
Working with arrays of effectful computations. Convert between []Effect[A] and Effect[[]A] for powerful composition patterns.
Core API
| Function | Signature | Description |
|---|---|---|
Sequence | func Sequence[A, F any](Applicative[F]) func([]HKT[F, A]) HKT[F, []A] | Flip array and effect |
Traverse | func Traverse[A, B, F any](Applicative[F]) func(func(A) HKT[F, B]) func([]A) HKT[F, []B] | Map and sequence |
TraverseWithIndex | func TraverseWithIndex[A, B, F any](Applicative[F]) func(func(int, A) HKT[F, B]) func([]A) HKT[F, []B] | Traverse with index |
Usage Examples
Sequence with Option
sequence_option.go
import (
A "github.com/IBM/fp-go/v2/array"
O "github.com/IBM/fp-go/v2/option"
F "github.com/IBM/fp-go/v2/function"
)
// Array of Options -> Option of Array
options := []O.Option[int]{
O.Some(1),
O.Some(2),
O.Some(3),
}
result := A.Sequence(O.Applicative[int]())(options)
// Some([]int{1, 2, 3})
// With None - short circuits
withNone := []O.Option[int]{
O.Some(1),
O.None[int](),
O.Some(3),
}
result2 := A.Sequence(O.Applicative[int]())(withNone)
// None - one None fails all
Sequence with Result
sequence_result.go
import Res "github.com/IBM/fp-go/v2/result"
// Array of Results -> Result of Array
results := []Res.Result[int]{
Res.Success(1),
Res.Success(2),
Res.Success(3),
}
combined := A.Sequence(Res.Applicative[error, int]())(results)
// Success([]int{1, 2, 3})
// With error
withError := []Res.Result[int]{
Res.Success(1),
Res.Error[int](errors.New("failed")),
Res.Success(3),
}
combined2 := A.Sequence(Res.Applicative[error, int]())(withError)
// Error("failed")
Traverse - Map and Sequence
traverse.go
type User struct {
ID int
Name string
}
ids := []int{1, 2, 3}
// Fetch users (returns Option)
users := A.Traverse(O.Applicative[User]())(
func(id int) O.Option[User] {
return fetchUser(id) // Returns Option[User]
},
)(ids)
// Option[[]User] - Some if all found, None if any missing
TraverseWithIndex
traverse_index.go
values := []string{"10", "20", "30"}
// Parse with index for error messages
parsed := A.TraverseWithIndex(Res.Applicative[error, int]())(
func(i int, s string) Res.Result[int] {
n, err := strconv.Atoi(s)
if err != nil {
return Res.Error[int](
fmt.Errorf("index %d: %w", i, err),
)
}
return Res.Success(n)
},
)(values)
// Result[[]int]
Validating All Items
validate.go
type Item struct {
ID int
Value string
}
items := []Item{
{ID: 1, Value: "valid"},
{ID: 2, Value: "also-valid"},
{ID: 3, Value: ""}, // Invalid
}
// Validate all items
validated := A.Traverse(Res.Applicative[error, Item]())(
func(item Item) Res.Result[Item] {
if item.Value == "" {
return Res.Error[Item](
fmt.Errorf("item %d: empty value", item.ID),
)
}
return Res.Success(item)
},
)(items)
// Error("item 3: empty value")
Parsing Configuration
parse_config.go
type Config struct {
Port int
Timeout int
Retries int
}
raw := []string{"8080", "30", "3"}
// Parse all values
parsed := A.Traverse(Res.Applicative[error, int]())(
func(s string) Res.Result[int] {
n, err := strconv.Atoi(s)
if err != nil {
return Res.Error[int](err)
}
return Res.Success(n)
},
)(raw)
// Build config from result
config := F.Pipe2(
parsed,
Res.Map(func(values []int) Config {
return Config{
Port: values[0],
Timeout: values[1],
Retries: values[2],
}
}),
)
// Result[Config]
Parallel API Calls
api.go
import IOE "github.com/IBM/fp-go/v2/ioeither"
type UserData struct {
ID int
Name string
}
userIDs := []int{1, 2, 3, 4, 5}
// Fetch all users
fetchAll := A.Traverse(IOE.Applicative[error, UserData]())(
func(id int) IOE.IOEither[error, UserData] {
return fetchUserAPI(id)
},
)(userIDs)
// IOEither[error, []UserData]
// Execute
users := fetchAll()
// Either[error, []UserData]
File Operations
files.go
import IO "github.com/IBM/fp-go/v2/io"
filenames := []string{"file1.txt", "file2.txt", "file3.txt"}
// Read all files
readAll := A.Traverse(IO.Applicative[[]byte]())(
func(name string) IO.IO[[]byte] {
return func() []byte {
data, _ := os.ReadFile(name)
return data
}
},
)(filenames)
// IO[[][]byte]
// Execute
contents := readAll()
// [][]byte - all file contents
Common Patterns
All or Nothing Processing
all_or_nothing.go
// Process all items - fail if any fails
func ProcessAll(items []string) Res.Result[[]int] {
return A.Traverse(Res.Applicative[error, int]())(
func(s string) Res.Result[int] {
return processItem(s)
},
)(items)
}
Batch Operations
batch.go
// Batch database inserts
func InsertAll(users []User) IOE.IOEither[error, []int] {
return A.Traverse(IOE.Applicative[error, int]())(
func(u User) IOE.IOEither[error, int] {
return insertUser(u)
},
)(users)
}
Conditional Processing
conditional.go
// Process only valid items
func ProcessValid(items []Item) O.Option[[]Result] {
return A.Traverse(O.Applicative[Result]())(
func(item Item) O.Option[Result] {
if item.IsValid() {
return O.Some(process(item))
}
return O.None[Result]()
},
)(items)
}
Collecting Results
collect.go
// Collect successful results, skip failures
func CollectSuccesses(items []string) []int {
results := A.Map(func(s string) O.Option[int] {
if n, err := strconv.Atoi(s); err == nil {
return O.Some(n)
}
return O.None[int]()
})(items)
// Filter out Nones
return A.FilterMap(F.Identity[O.Option[int]])(results)
}
Parallel with Limit
parallel_limit.go
// Process in batches to limit parallelism
func ProcessInBatches(items []Item, batchSize int) IOE.IOEither[error, []Result] {
batches := chunkArray(items, batchSize)
return A.Traverse(IOE.Applicative[error, []Result]())(
func(batch []Item) IOE.IOEither[error, []Result] {
return A.Traverse(IOE.Applicative[error, Result]())(
processItem,
)(batch)
},
)(batches)
}
Error Accumulation
errors.go
// Collect all errors instead of short-circuiting
type ValidationResult struct {
Valid []Item
Errors []error
}
func ValidateAll(items []Item) ValidationResult {
var valid []Item
var errors []error
for _, item := range items {
if err := validate(item); err != nil {
errors = append(errors, err)
} else {
valid = append(valid, item)
}
}
return ValidationResult{Valid: valid, Errors: errors}
}
Short-Circuit Behavior: Both Sequence and Traverse short-circuit on the first failure:
- With Option: first None returns None
- With Result: first Error returns Error
- With IOEither: first Left returns Left
Use Cases: Traverse is ideal for:
- Validating all array elements
- Batch API requests
- Parallel file operations
- All-or-nothing transformations
- Converting imperative loops to functional pipelines
Performance: Traverse operations process elements sequentially by default. For true parallelism, use specialized parallel execution utilities or batch processing patterns.