1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-23 22:14:53 +02:00

fix: bind docs

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2025-11-06 16:18:15 +01:00
parent 178df09ff7
commit b3bd5e9ad3
16 changed files with 783 additions and 32 deletions

View File

@@ -21,14 +21,56 @@ import (
F "github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
// result := generic.Do[[]State, State](State{})
func Do[GS ~[]S, S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
// For arrays, this produces the cartesian product where later steps can use values from earlier steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
//
// result := F.Pipe2(
// generic.Do[[]State, State](State{}),
// generic.Bind[[]State, []State, []int, State, State, int](
// func(x int) func(State) State {
// return func(s State) State { s.X = x; return s }
// },
// func(s State) []int {
// return []int{1, 2, 3}
// },
// ),
// generic.Bind[[]State, []State, []int, State, State, int](
// func(y int) func(State) State {
// return func(s State) State { s.Y = y; return s }
// },
// func(s State) []int {
// // This can access s.X from the previous step
// return []int{s.X * 10, s.X * 20}
// },
// ),
// ) // Produces: {1,10}, {1,20}, {2,20}, {2,40}, {3,30}, {3,60}
func Bind[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,

View File

@@ -21,14 +21,63 @@ import (
G "github.com/IBM/fp-go/v2/readereither/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// UserID string
// TenantID string
// }
// result := readereither.Do(State{})
func Do[S any](
empty S,
) ReaderEither[S] {
return G.Do[ReaderEither[S], context.Context, error, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the context.Context from the environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// UserID string
// TenantID string
// }
//
// result := F.Pipe2(
// readereither.Do(State{}),
// readereither.Bind(
// func(uid string) func(State) State {
// return func(s State) State { s.UserID = uid; return s }
// },
// func(s State) readereither.ReaderEither[string] {
// return func(ctx context.Context) either.Either[error, string] {
// if uid, ok := ctx.Value("userID").(string); ok {
// return either.Right[error](uid)
// }
// return either.Left[string](errors.New("no userID"))
// }
// },
// ),
// readereither.Bind(
// func(tid string) func(State) State {
// return func(s State) State { s.TenantID = tid; return s }
// },
// func(s State) readereither.ReaderEither[string] {
// // This can access s.UserID from the previous step
// return func(ctx context.Context) either.Either[error, string] {
// return either.Right[error]("tenant-" + s.UserID)
// }
// },
// ),
// )
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderEither[T],

View File

@@ -21,14 +21,64 @@ import (
"github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
// result := readerioeither.Do(State{})
func Do[S any](
empty S,
) ReaderIOEither[S] {
return Of(empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the context.Context from the environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
//
// result := F.Pipe2(
// readerioeither.Do(State{}),
// readerioeither.Bind(
// func(user User) func(State) State {
// return func(s State) State { s.User = user; return s }
// },
// func(s State) readerioeither.ReaderIOEither[User] {
// return func(ctx context.Context) ioeither.IOEither[error, User] {
// return ioeither.TryCatch(func() (User, error) {
// return fetchUser(ctx)
// })
// }
// },
// ),
// readerioeither.Bind(
// func(cfg Config) func(State) State {
// return func(s State) State { s.Config = cfg; return s }
// },
// func(s State) readerioeither.ReaderIOEither[Config] {
// // This can access s.User from the previous step
// return func(ctx context.Context) ioeither.IOEither[error, Config] {
// return ioeither.TryCatch(func() (Config, error) {
// return fetchConfigForUser(ctx, s.User.ID)
// })
// }
// },
// ),
// )
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderIOEither[T],

View File

@@ -21,14 +21,55 @@ import (
F "github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
// result := identity.Do(State{})
func Do[S any](
empty S,
) S {
return empty
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
//
// result := F.Pipe2(
// identity.Do(State{}),
// identity.Bind(
// func(x int) func(State) State {
// return func(s State) State { s.X = x; return s }
// },
// func(s State) int {
// return 42
// },
// ),
// identity.Bind(
// func(y int) func(State) State {
// return func(s State) State { s.Y = y; return s }
// },
// func(s State) int {
// // This can access s.X from the previous step
// return s.X * 2
// },
// ),
// ) // State{X: 42, Y: 84}
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,

View File

@@ -21,14 +21,59 @@ import (
"github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// User User
// Posts []Post
// }
// result := ioeither.Do[error](State{})
func Do[E, S any](
empty S,
) IOEither[E, S] {
return Of[E](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// User User
// Posts []Post
// }
//
// result := F.Pipe2(
// ioeither.Do[error](State{}),
// ioeither.Bind(
// func(user User) func(State) State {
// return func(s State) State { s.User = user; return s }
// },
// func(s State) ioeither.IOEither[error, User] {
// return ioeither.TryCatch(func() (User, error) {
// return fetchUser()
// })
// },
// ),
// ioeither.Bind(
// func(posts []Post) func(State) State {
// return func(s State) State { s.Posts = posts; return s }
// },
// func(s State) ioeither.IOEither[error, []Post] {
// // This can access s.User from the previous step
// return ioeither.TryCatch(func() ([]Post, error) {
// return fetchPostsForUser(s.User.ID)
// })
// },
// ),
// )
func Bind[E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IOEither[E, T],

View File

@@ -21,14 +21,55 @@ import (
"github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// Name string
// Age int
// }
// result := iooption.Do(State{})
func Do[S any](
empty S,
) IOOption[S] {
return Of(empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// Name string
// Age int
// }
//
// result := F.Pipe2(
// iooption.Do(State{}),
// iooption.Bind(
// func(name string) func(State) State {
// return func(s State) State { s.Name = name; return s }
// },
// func(s State) iooption.IOOption[string] {
// return iooption.FromIO(io.Of("Alice"))
// },
// ),
// iooption.Bind(
// func(age int) func(State) State {
// return func(s State) State { s.Age = age; return s }
// },
// func(s State) iooption.IOOption[int] {
// // This can access s.Name from the previous step
// return iooption.FromIO(io.Of(len(s.Name) * 10))
// },
// ),
// )
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IOOption[T],

View File

@@ -19,14 +19,56 @@ import (
G "github.com/IBM/fp-go/v2/iterator/stateless/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
// result := stateless.Do(State{})
func Do[S any](
empty S,
) Iterator[S] {
return G.Do[Iterator[S]](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
// For iterators, this produces the cartesian product of all values.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
//
// result := F.Pipe2(
// stateless.Do(State{}),
// stateless.Bind(
// func(x int) func(State) State {
// return func(s State) State { s.X = x; return s }
// },
// func(s State) stateless.Iterator[int] {
// return stateless.Of(1, 2, 3)
// },
// ),
// stateless.Bind(
// func(y int) func(State) State {
// return func(s State) State { s.Y = y; return s }
// },
// func(s State) stateless.Iterator[int] {
// // This can access s.X from the previous step
// return stateless.Of(s.X * 10, s.X * 20)
// },
// ),
// ) // Produces: {1,10}, {1,20}, {2,20}, {2,40}, {3,30}, {3,60}
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) Iterator[T],

View File

@@ -23,14 +23,56 @@ import (
P "github.com/IBM/fp-go/v2/pair"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
// result := generic.Do[Iterator[State]](State{})
func Do[GS ~func() O.Option[P.Pair[GS, S]], S any](
empty S,
) GS {
return Of[GS](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
// For iterators, this produces the cartesian product where later steps can use values from earlier steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// X int
// Y int
// }
//
// result := F.Pipe2(
// generic.Do[Iterator[State]](State{}),
// generic.Bind[Iterator[State], Iterator[State], Iterator[int], State, State, int](
// func(x int) func(State) State {
// return func(s State) State { s.X = x; return s }
// },
// func(s State) Iterator[int] {
// return generic.Of[Iterator[int]](1, 2, 3)
// },
// ),
// generic.Bind[Iterator[State], Iterator[State], Iterator[int], State, State, int](
// func(y int) func(State) State {
// return func(s State) State { s.Y = y; return s }
// },
// func(s State) Iterator[int] {
// // This can access s.X from the previous step
// return generic.Of[Iterator[int]](s.X * 10, s.X * 20)
// },
// ),
// ) // Produces: {1,10}, {1,20}, {2,20}, {2,40}, {3,30}, {3,60}
func Bind[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], GA ~func() O.Option[P.Pair[GA, A]], S1, S2, A any](
setter func(A) func(S1) S2,
f func(S1) GA,

View File

@@ -19,14 +19,55 @@ import (
"github.com/IBM/fp-go/v2/io"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// Config Config
// Data Data
// }
// result := lazy.Do(State{})
func Do[S any](
empty S,
) Lazy[S] {
return io.Do(empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// Config Config
// Data Data
// }
//
// result := F.Pipe2(
// lazy.Do(State{}),
// lazy.Bind(
// func(cfg Config) func(State) State {
// return func(s State) State { s.Config = cfg; return s }
// },
// func(s State) lazy.Lazy[Config] {
// return lazy.MakeLazy(func() Config { return loadConfig() })
// },
// ),
// lazy.Bind(
// func(data Data) func(State) State {
// return func(s State) State { s.Data = data; return s }
// },
// func(s State) lazy.Lazy[Data] {
// // This can access s.Config from the previous step
// return lazy.MakeLazy(func() Data { return loadData(s.Config) })
// },
// ),
// )
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) Lazy[T],

View File

@@ -19,14 +19,68 @@ import (
G "github.com/IBM/fp-go/v2/readereither/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
// type Env struct {
// UserService UserService
// ConfigService ConfigService
// }
// result := readereither.Do[Env, error](State{})
func Do[R, E, S any](
empty S,
) ReaderEither[R, E, S] {
return G.Do[ReaderEither[R, E, S], R, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the shared environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
// type Env struct {
// UserService UserService
// ConfigService ConfigService
// }
//
// result := F.Pipe2(
// readereither.Do[Env, error](State{}),
// readereither.Bind(
// func(user User) func(State) State {
// return func(s State) State { s.User = user; return s }
// },
// func(s State) readereither.ReaderEither[Env, error, User] {
// return readereither.Asks(func(env Env) either.Either[error, User] {
// return env.UserService.GetUser()
// })
// },
// ),
// readereither.Bind(
// func(cfg Config) func(State) State {
// return func(s State) State { s.Config = cfg; return s }
// },
// func(s State) readereither.ReaderEither[Env, error, Config] {
// // This can access s.User from the previous step
// return readereither.Asks(func(env Env) either.Either[error, Config] {
// return env.ConfigService.GetConfigForUser(s.User.ID)
// })
// },
// ),
// )
func Bind[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderEither[R, E, T],

View File

@@ -22,14 +22,68 @@ import (
F "github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// Config Config
// User User
// }
// type Env struct {
// ConfigService ConfigService
// UserService UserService
// }
// result := generic.Do[ReaderEither[Env, error, State], Env, error, State](State{})
func Do[GS ~func(R) ET.Either[E, S], R, E, S any](
empty S,
) GS {
return Of[GS, E, R, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the shared environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// Config Config
// User User
// }
// type Env struct {
// ConfigService ConfigService
// UserService UserService
// }
//
// result := F.Pipe2(
// generic.Do[ReaderEither[Env, error, State], Env, error, State](State{}),
// generic.Bind[ReaderEither[Env, error, State], ReaderEither[Env, error, State], ReaderEither[Env, error, Config], Env, error, State, State, Config](
// func(cfg Config) func(State) State {
// return func(s State) State { s.Config = cfg; return s }
// },
// func(s State) ReaderEither[Env, error, Config] {
// return func(env Env) either.Either[error, Config] {
// return env.ConfigService.Load()
// }
// },
// ),
// generic.Bind[ReaderEither[Env, error, State], ReaderEither[Env, error, State], ReaderEither[Env, error, User], Env, error, State, State, User](
// func(user User) func(State) State {
// return func(s State) State { s.User = user; return s }
// },
// func(s State) ReaderEither[Env, error, User] {
// // This can access s.Config from the previous step
// return func(env Env) either.Either[error, User] {
// return env.UserService.GetUserForConfig(s.Config)
// }
// },
// ),
// )
func Bind[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(R) ET.Either[E, T], R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GT,

View File

@@ -21,14 +21,68 @@ import (
"github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// Host string
// Port int
// }
// type Config struct {
// DefaultHost string
// DefaultPort int
// }
// result := readerio.Do[Config](State{})
func Do[R, S any](
empty S,
) ReaderIO[R, S] {
return Of[R](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the shared environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// Host string
// Port int
// }
// type Config struct {
// DefaultHost string
// DefaultPort int
// }
//
// result := F.Pipe2(
// readerio.Do[Config](State{}),
// readerio.Bind(
// func(host string) func(State) State {
// return func(s State) State { s.Host = host; return s }
// },
// func(s State) readerio.ReaderIO[Config, string] {
// return readerio.Asks(func(c Config) io.IO[string] {
// return io.Of(c.DefaultHost)
// })
// },
// ),
// readerio.Bind(
// func(port int) func(State) State {
// return func(s State) State { s.Port = port; return s }
// },
// func(s State) readerio.ReaderIO[Config, int] {
// // This can access s.Host from the previous step
// return readerio.Asks(func(c Config) io.IO[int] {
// return io.Of(c.DefaultPort)
// })
// },
// ),
// )
func Bind[R, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderIO[R, T],

View File

@@ -20,14 +20,68 @@ import (
G "github.com/IBM/fp-go/v2/readerioeither/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// User User
// Posts []Post
// }
// type Env struct {
// UserRepo UserRepository
// PostRepo PostRepository
// }
// result := readerioeither.Do[Env, error](State{})
func Do[R, E, S any](
empty S,
) ReaderIOEither[R, E, S] {
return G.Do[ReaderIOEither[R, E, S], IOE.IOEither[E, S], R, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the shared environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// User User
// Posts []Post
// }
// type Env struct {
// UserRepo UserRepository
// PostRepo PostRepository
// }
//
// result := F.Pipe2(
// readerioeither.Do[Env, error](State{}),
// readerioeither.Bind(
// func(user User) func(State) State {
// return func(s State) State { s.User = user; return s }
// },
// func(s State) readerioeither.ReaderIOEither[Env, error, User] {
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
// return env.UserRepo.FindUser()
// })
// },
// ),
// readerioeither.Bind(
// func(posts []Post) func(State) State {
// return func(s State) State { s.Posts = posts; return s }
// },
// func(s State) readerioeither.ReaderIOEither[Env, error, []Post] {
// // This can access s.User from the previous step
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
// return env.PostRepo.FindPostsByUser(s.User.ID)
// })
// },
// ),
// )
func Bind[R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) ReaderIOEither[R, E, T],

View File

@@ -22,14 +22,68 @@ import (
F "github.com/IBM/fp-go/v2/internal/functor"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
// type Env struct {
// UserRepo UserRepository
// ConfigRepo ConfigRepository
// }
// result := generic.Do[ReaderIOEither[Env, error, State], IOEither[error, State], Env, error, State](State{})
func Do[GRS ~func(R) GS, GS ~func() either.Either[E, S], R, E, S any](
empty S,
) GRS {
return Of[GRS, GS, R, E, S](empty)
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps
// and access the shared environment.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
// type Env struct {
// UserRepo UserRepository
// ConfigRepo ConfigRepository
// }
//
// result := F.Pipe2(
// generic.Do[ReaderIOEither[Env, error, State], IOEither[error, State], Env, error, State](State{}),
// generic.Bind[ReaderIOEither[Env, error, State], ReaderIOEither[Env, error, State], ReaderIOEither[Env, error, User], IOEither[error, State], IOEither[error, State], IOEither[error, User], Env, error, State, State, User](
// func(user User) func(State) State {
// return func(s State) State { s.User = user; return s }
// },
// func(s State) ReaderIOEither[Env, error, User] {
// return func(env Env) ioeither.IOEither[error, User] {
// return env.UserRepo.FindUser()
// }
// },
// ),
// generic.Bind[ReaderIOEither[Env, error, State], ReaderIOEither[Env, error, State], ReaderIOEither[Env, error, Config], IOEither[error, State], IOEither[error, State], IOEither[error, Config], Env, error, State, State, Config](
// func(cfg Config) func(State) State {
// return func(s State) State { s.Config = cfg; return s }
// },
// func(s State) ReaderIOEither[Env, error, Config] {
// // This can access s.User from the previous step
// return func(env Env) ioeither.IOEither[error, Config] {
// return env.ConfigRepo.LoadConfigForUser(s.User.ID)
// }
// },
// ),
// )
func Bind[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GS1 ~func() either.Either[E, S1], GS2 ~func() either.Either[E, S2], GT ~func() either.Either[E, T], R, E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) GRT,

View File

@@ -20,12 +20,54 @@ import (
G "github.com/IBM/fp-go/v2/record/generic"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// Name string
// Count int
// }
// result := record.Do[string, State]()
func Do[K comparable, S any]() map[K]S {
return G.Do[map[K]S, K, S]()
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
// For records, this merges values by key.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// Name string
// Count int
// }
//
// result := F.Pipe2(
// record.Do[string, State](),
// record.Bind(monoid.Record[string, State]())(
// func(name string) func(State) State {
// return func(s State) State { s.Name = name; return s }
// },
// func(s State) map[string]string {
// return map[string]string{"a": "Alice", "b": "Bob"}
// },
// ),
// record.Bind(monoid.Record[string, State]())(
// func(count int) func(State) State {
// return func(s State) State { s.Count = count; return s }
// },
// func(s State) map[string]int {
// // This can access s.Name from the previous step
// return map[string]int{"a": len(s.Name), "b": len(s.Name) * 2}
// },
// ),
// )
func Bind[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, f func(S1) map[K]T) func(map[K]S1) map[K]S2 {
return G.Bind[map[K]S1, map[K]S2, map[K]T, K, S1, S2, T](m)
}

View File

@@ -22,12 +22,58 @@ import (
Mo "github.com/IBM/fp-go/v2/monoid"
)
// Bind creates an empty context of type [S] to be used with the [Bind] operation
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// Name string
// Count int
// }
// result := generic.Do[map[string]State, string, State]()
func Do[GS ~map[K]S, K comparable, S any]() GS {
return Empty[GS, K, S]()
}
// Bind attaches the result of a computation to a context [S1] to produce a context [S2]
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
// This enables sequential composition where each step can depend on the results of previous steps.
// For records, this merges values by key where later steps can use values from earlier steps.
//
// The setter function takes the result of the computation and returns a function that
// updates the context from S1 to S2.
//
// Example:
//
// type State struct {
// Name string
// Count int
// }
//
// result := F.Pipe2(
// generic.Do[map[string]State, string, State](),
// generic.Bind[map[string]State, map[string]State, map[string]string, string, State, State, string](
// monoid.Record[string, State](),
// )(
// func(name string) func(State) State {
// return func(s State) State { s.Name = name; return s }
// },
// func(s State) map[string]string {
// return map[string]string{"a": "Alice", "b": "Bob"}
// },
// ),
// generic.Bind[map[string]State, map[string]State, map[string]int, string, State, State, int](
// monoid.Record[string, State](),
// )(
// func(count int) func(State) State {
// return func(s State) State { s.Count = count; return s }
// },
// func(s State) map[string]int {
// // This can access s.Name from the previous step
// return map[string]int{"a": len(s.Name), "b": len(s.Name) * 2}
// },
// ),
// )
func Bind[GS1 ~map[K]S1, GS2 ~map[K]S2, GT ~map[K]T, K comparable, S1, S2, T any](m Mo.Monoid[GS2]) func(setter func(T) func(S1) S2, f func(S1) GT) func(GS1) GS2 {
c := Chain[GS1, GS2, K, S1, S2](m)
return func(setter func(T) func(S1) S2, f func(S1) GT) func(GS1) GS2 {