mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
318 lines
7.3 KiB
Markdown
318 lines
7.3 KiB
Markdown
# fp-go V2: Enhanced Functional Programming for Go 1.24+
|
|
|
|
[](https://pkg.go.dev/github.com/IBM/fp-go/v2)
|
|
|
|
Version 2 of fp-go leverages [generic type aliases](https://github.com/golang/go/issues/46477) introduced in Go 1.24, providing a more ergonomic and streamlined API.
|
|
|
|
## 📚 Table of Contents
|
|
|
|
- [Requirements](#-requirements)
|
|
- [Breaking Changes](#-breaking-changes)
|
|
- [Key Improvements](#-key-improvements)
|
|
- [Migration Guide](#-migration-guide)
|
|
- [Installation](#-installation)
|
|
- [What's New](#-whats-new)
|
|
|
|
## 🔧 Requirements
|
|
|
|
- **Go 1.24 or later** (for generic type alias support)
|
|
|
|
## ⚠️ Breaking Changes
|
|
|
|
### 1. Generic Type Aliases
|
|
|
|
V2 uses [generic type aliases](https://github.com/golang/go/issues/46477) which require Go 1.24+. This is the most significant change and enables cleaner type definitions.
|
|
|
|
**V1:**
|
|
```go
|
|
type ReaderIOEither[R, E, A any] RD.Reader[R, IOE.IOEither[E, A]]
|
|
```
|
|
|
|
**V2:**
|
|
```go
|
|
type ReaderIOEither[R, E, A any] = RD.Reader[R, IOE.IOEither[E, A]]
|
|
```
|
|
|
|
### 2. Generic Type Parameter Ordering
|
|
|
|
Type parameters that **cannot** be inferred from function arguments now come first, improving type inference.
|
|
|
|
**V1:**
|
|
```go
|
|
// Ap in V1 - less intuitive ordering
|
|
func Ap[R, E, A, B any](fa ReaderIOEither[R, E, A]) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B]
|
|
```
|
|
|
|
**V2:**
|
|
```go
|
|
// Ap in V2 - B comes first as it cannot be inferred
|
|
func Ap[B, R, E, A any](fa ReaderIOEither[R, E, A]) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B]
|
|
```
|
|
|
|
This change allows the Go compiler to infer more types automatically, reducing the need for explicit type parameters.
|
|
|
|
### 3. Pair Monad Semantics
|
|
|
|
Monadic operations for `Pair` now operate on the **second argument** to align with the [Haskell definition](https://hackage.haskell.org/package/TypeCompose-0.9.14/docs/Data-Pair.html).
|
|
|
|
**V1:**
|
|
```go
|
|
// Operations on first element
|
|
pair := MakePair(1, "hello")
|
|
result := Map(func(x int) int { return x * 2 })(pair) // Pair(2, "hello")
|
|
```
|
|
|
|
**V2:**
|
|
```go
|
|
// Operations on second element (Haskell-compatible)
|
|
pair := MakePair(1, "hello")
|
|
result := Map(func(s string) string { return s + "!" })(pair) // Pair(1, "hello!")
|
|
```
|
|
|
|
## ✨ Key Improvements
|
|
|
|
### 1. Simplified Type Declarations
|
|
|
|
Generic type aliases eliminate the need for namespace imports in type declarations.
|
|
|
|
**V1 Approach:**
|
|
```go
|
|
import (
|
|
ET "github.com/IBM/fp-go/either"
|
|
OPT "github.com/IBM/fp-go/option"
|
|
)
|
|
|
|
func processData(input string) ET.Either[error, OPT.Option[int]] {
|
|
// implementation
|
|
}
|
|
```
|
|
|
|
**V2 Approach:**
|
|
```go
|
|
import (
|
|
"github.com/IBM/fp-go/v2/either"
|
|
"github.com/IBM/fp-go/v2/option"
|
|
)
|
|
|
|
// Define type aliases once
|
|
type Either[A any] = either.Either[error, A]
|
|
type Option[A any] = option.Option[A]
|
|
|
|
// Use them throughout your codebase
|
|
func processData(input string) Either[Option[int]] {
|
|
// implementation
|
|
}
|
|
```
|
|
|
|
### 2. No More `generic` Subpackages
|
|
|
|
The library implementation no longer requires separate `generic` subpackages, making the codebase simpler and easier to understand.
|
|
|
|
**V1 Structure:**
|
|
```
|
|
either/
|
|
either.go
|
|
generic/
|
|
either.go // Generic implementation
|
|
```
|
|
|
|
**V2 Structure:**
|
|
```
|
|
either/
|
|
either.go // Single, clean implementation
|
|
```
|
|
|
|
### 3. Better Type Inference
|
|
|
|
The reordered type parameters allow the Go compiler to infer more types automatically:
|
|
|
|
**V1:**
|
|
```go
|
|
// Often need explicit type parameters
|
|
result := Map[Context, error, int, string](transform)(value)
|
|
```
|
|
|
|
**V2:**
|
|
```go
|
|
// Compiler can infer more types
|
|
result := Map(transform)(value) // Cleaner!
|
|
```
|
|
|
|
## 🚀 Migration Guide
|
|
|
|
### Step 1: Update Go Version
|
|
|
|
Ensure you're using Go 1.24 or later:
|
|
|
|
```bash
|
|
go version # Should show go1.24 or higher
|
|
```
|
|
|
|
### Step 2: Update Import Paths
|
|
|
|
Change all import paths from `github.com/IBM/fp-go` to `github.com/IBM/fp-go/v2`:
|
|
|
|
**Before:**
|
|
```go
|
|
import (
|
|
"github.com/IBM/fp-go/either"
|
|
"github.com/IBM/fp-go/option"
|
|
)
|
|
```
|
|
|
|
**After:**
|
|
```go
|
|
import (
|
|
"github.com/IBM/fp-go/v2/either"
|
|
"github.com/IBM/fp-go/v2/option"
|
|
)
|
|
```
|
|
|
|
### Step 3: Remove `generic` Subpackage Imports
|
|
|
|
If you were using generic subpackages, remove them:
|
|
|
|
**Before:**
|
|
```go
|
|
import (
|
|
E "github.com/IBM/fp-go/either/generic"
|
|
)
|
|
```
|
|
|
|
**After:**
|
|
```go
|
|
import (
|
|
"github.com/IBM/fp-go/v2/either"
|
|
)
|
|
```
|
|
|
|
### Step 4: Update Type Parameter Order
|
|
|
|
Review functions like `Ap` where type parameter order has changed. The compiler will help identify these:
|
|
|
|
**Before:**
|
|
```go
|
|
result := Ap[Context, error, int, string](value)(funcInContext)
|
|
```
|
|
|
|
**After:**
|
|
```go
|
|
result := Ap[string, Context, error, int](value)(funcInContext)
|
|
// Or better yet, let the compiler infer:
|
|
result := Ap(value)(funcInContext)
|
|
```
|
|
|
|
### Step 5: Update Pair Operations
|
|
|
|
If you're using `Pair`, update operations to work on the second element:
|
|
|
|
**Before (V1):**
|
|
```go
|
|
pair := MakePair(42, "data")
|
|
// Map operates on first element
|
|
result := Map(func(x int) int { return x * 2 })(pair)
|
|
```
|
|
|
|
**After (V2):**
|
|
```go
|
|
pair := MakePair(42, "data")
|
|
// Map operates on second element
|
|
result := Map(func(s string) string { return s + "!" })(pair)
|
|
```
|
|
|
|
### Step 6: Simplify Type Aliases
|
|
|
|
Create project-wide type aliases for common patterns:
|
|
|
|
```go
|
|
// types.go - Define once, use everywhere
|
|
package myapp
|
|
|
|
import (
|
|
"github.com/IBM/fp-go/v2/either"
|
|
"github.com/IBM/fp-go/v2/option"
|
|
"github.com/IBM/fp-go/v2/ioeither"
|
|
)
|
|
|
|
type Either[A any] = either.Either[error, A]
|
|
type Option[A any] = option.Option[A]
|
|
type IOEither[A any] = ioeither.IOEither[error, A]
|
|
```
|
|
|
|
## 📦 Installation
|
|
|
|
```bash
|
|
go get github.com/IBM/fp-go/v2
|
|
```
|
|
|
|
## 🆕 What's New
|
|
|
|
### Cleaner API Surface
|
|
|
|
The elimination of `generic` subpackages means:
|
|
- Fewer imports to manage
|
|
- Simpler package structure
|
|
- Easier to navigate documentation
|
|
- More intuitive API
|
|
|
|
### Example: Before and After
|
|
|
|
**V1 Complex Example:**
|
|
```go
|
|
import (
|
|
ET "github.com/IBM/fp-go/either"
|
|
EG "github.com/IBM/fp-go/either/generic"
|
|
IOET "github.com/IBM/fp-go/ioeither"
|
|
IOEG "github.com/IBM/fp-go/ioeither/generic"
|
|
)
|
|
|
|
func process() IOET.IOEither[error, string] {
|
|
return IOEG.Map[error, int, string](
|
|
func(x int) string { return fmt.Sprintf("%d", x) },
|
|
)(fetchData())
|
|
}
|
|
```
|
|
|
|
**V2 Simplified Example:**
|
|
```go
|
|
import (
|
|
"github.com/IBM/fp-go/v2/either"
|
|
"github.com/IBM/fp-go/v2/ioeither"
|
|
)
|
|
|
|
type IOEither[A any] = ioeither.IOEither[error, A]
|
|
|
|
func process() IOEither[string] {
|
|
return ioeither.Map(
|
|
func(x int) string { return fmt.Sprintf("%d", x) },
|
|
)(fetchData())
|
|
}
|
|
```
|
|
|
|
## 📚 Additional Resources
|
|
|
|
- [Main README](../README.md) - Core concepts and design philosophy
|
|
- [API Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2)
|
|
- [Code Samples](../samples/)
|
|
- [Go 1.24 Release Notes](https://tip.golang.org/doc/go1.24)
|
|
|
|
## 🤔 Should I Migrate?
|
|
|
|
**Migrate to V2 if:**
|
|
- ✅ You can use Go 1.24+
|
|
- ✅ You want cleaner, more maintainable code
|
|
- ✅ You want better type inference
|
|
- ✅ You're starting a new project
|
|
|
|
**Stay on V1 if:**
|
|
- ⚠️ You're locked to Go < 1.24
|
|
- ⚠️ Migration effort outweighs benefits for your project
|
|
- ⚠️ You need stability in production (V2 is newer)
|
|
|
|
## 🐛 Issues and Feedback
|
|
|
|
Found a bug or have a suggestion? Please [open an issue](https://github.com/IBM/fp-go/issues) on GitHub.
|
|
|
|
## 📄 License
|
|
|
|
This project is licensed under the Apache License 2.0 - see the LICENSE file for details. |