1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-19 23:42:05 +02:00
Files
fp-go/v2/idiomatic/context/readerresult/bind.go
Dr. Carsten Leue fceda15701 doc: improve docs
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-12-17 10:11:58 +01:00

457 lines
14 KiB
Go

// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package readerresult
import (
"context"
RR "github.com/IBM/fp-go/v2/idiomatic/readerresult"
"github.com/IBM/fp-go/v2/idiomatic/result"
AP "github.com/IBM/fp-go/v2/internal/apply"
C "github.com/IBM/fp-go/v2/internal/chain"
"github.com/IBM/fp-go/v2/reader"
RES "github.com/IBM/fp-go/v2/result"
)
// Do initializes a do-notation context with an empty state.
//
// This is the starting point for do-notation style composition, which allows
// imperative-style sequencing of ReaderResult computations while maintaining
// functional purity.
//
// Type Parameters:
// - S: The state type
//
// Parameters:
// - empty: The initial empty state
//
// Returns:
// - A ReaderResult[S] containing the initial state
//
//go:inline
func Do[S any](
empty S,
) ReaderResult[S] {
return RR.Do[context.Context](empty)
}
// Bind sequences an EFFECTFUL ReaderResult computation and updates the state with its result.
//
// IMPORTANT: Bind is for EFFECTFUL FUNCTIONS that depend on context.Context.
// The Kleisli parameter (State -> ReaderResult[T]) is effectful because ReaderResult
// depends on context.Context (can be cancelled, has deadlines, carries values).
//
// For PURE FUNCTIONS (side-effect free), use:
// - BindResultK: For pure functions with errors (State -> (Value, error))
// - Let: For pure functions without errors (State -> Value)
//
// This is the core operation for do-notation, allowing you to chain computations
// where each step can depend on the accumulated state and update it with new values.
//
// Type Parameters:
// - S1: The input state type
// - S2: The output state type
// - T: The type of value produced by the computation
//
// Parameters:
// - setter: A function that takes the computation result and returns a state updater
// - f: A Kleisli arrow that produces the next effectful computation based on current state
//
// Returns:
// - An Operator that transforms ReaderResult[S1] to ReaderResult[S2]
//
//go:inline
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f Kleisli[S1, T],
) Operator[S1, S2] {
return C.Bind(
Chain[S1, S2],
Map[T, S2],
setter,
WithContextK(f),
)
}
// Let attaches the result of a PURE computation to a state.
//
// IMPORTANT: Let is for PURE FUNCTIONS (side-effect free) that don't depend on context.Context.
// The function parameter (State -> Value) is pure - it only reads from state with no effects.
//
// For EFFECTFUL FUNCTIONS (that need context.Context), use:
// - Bind: For effectful ReaderResult computations (State -> ReaderResult[Value])
//
// For PURE FUNCTIONS with error handling, use:
// - BindResultK: For pure functions with errors (State -> (Value, error))
//
// Unlike Bind, Let works with pure functions (not ReaderResult computations).
// This is useful for deriving values from the current state without performing
// any effects.
//
// Type Parameters:
// - S1: The input state type
// - S2: The output state type
// - T: The type of value computed
//
// Parameters:
// - setter: A function that takes the computed value and returns a state updater
// - f: A pure function that computes a value from the current state
//
// Returns:
// - An Operator that transforms ReaderResult[S1] to ReaderResult[S2]
//
//go:inline
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) Operator[S1, S2] {
return RR.Let[context.Context](setter, f)
}
// LetTo attaches a constant value to a state.
// This is a PURE operation (side-effect free).
//
// This is a simplified version of Let for when you want to add a constant
// value to the state without computing it.
//
// Type Parameters:
// - S1: The input state type
// - S2: The output state type
// - T: The type of the constant value
//
// Parameters:
// - setter: A function that takes the constant and returns a state updater
// - b: The constant value to attach
//
// Returns:
// - An Operator that transforms ReaderResult[S1] to ReaderResult[S2]
//
//go:inline
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) Operator[S1, S2] {
return RR.LetTo[context.Context](setter, b)
}
// BindTo initializes do-notation by binding a value to a state.
//
// This is typically used as the first operation after a computation to
// start building up a state structure.
//
// Type Parameters:
// - S1: The state type to create
// - T: The type of the initial value
//
// Parameters:
// - setter: A function that creates the initial state from a value
//
// Returns:
// - An Operator that transforms ReaderResult[T] to ReaderResult[S1]
//
//go:inline
func BindTo[S1, T any](
setter func(T) S1,
) Operator[T, S1] {
return RR.BindTo[context.Context](setter)
}
// BindToP initializes do-notation by binding a value to a state using a Prism.
//
// This is a variant of BindTo that uses a prism instead of a setter function.
// Prisms are useful for working with sum types and optional values.
//
// Type Parameters:
// - S1: The state type to create
// - T: The type of the initial value
//
// Parameters:
// - setter: A prism that can construct the state from a value
//
// Returns:
// - An Operator that transforms ReaderResult[T] to ReaderResult[S1]
//
//go:inline
func BindToP[S1, T any](
setter Prism[S1, T],
) Operator[T, S1] {
return BindTo(setter.ReverseGet)
}
// ApS attaches a value to a context using applicative style.
//
// IMPORTANT: ApS is for EFFECTFUL FUNCTIONS that depend on context.Context.
// The ReaderResult parameter is effectful because it depends on context.Context.
//
// Unlike Bind (which sequences operations), ApS can be used when operations are
// independent and can conceptually run in parallel.
//
// Type Parameters:
// - S1: The input state type
// - S2: The output state type
// - T: The type of value produced by the computation
//
// Parameters:
// - setter: A function that takes the computation result and returns a state updater
// - fa: An effectful ReaderResult computation
//
// Returns:
// - An Operator that transforms ReaderResult[S1] to ReaderResult[S2]
//
//go:inline
func ApS[S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderResult[T],
) Operator[S1, S2] {
return AP.ApS(
Ap[S2, T],
Map[S1, func(T) S2],
setter,
fa,
)
}
// ApSL is a variant of ApS that uses a lens to focus on a specific field in the state.
//
// IMPORTANT: ApSL is for EFFECTFUL FUNCTIONS that depend on context.Context.
// The ReaderResult parameter is effectful because it depends on context.Context.
//
// Instead of providing a setter function, you provide a lens that knows how to get and set
// the field. This is more convenient when working with nested structures.
//
// Type Parameters:
// - S: The state type
// - T: The type of the field to update
//
// Parameters:
// - lens: A lens that focuses on a field of type T within state S
// - fa: An effectful ReaderResult computation that produces a value of type T
//
// Returns:
// - An Operator that transforms ReaderResult[S] to ReaderResult[S]
//
//go:inline
func ApSL[S, T any](
lens Lens[S, T],
fa ReaderResult[T],
) Operator[S, S] {
return ApS(lens.Set, fa)
}
// BindL is a variant of Bind that uses a lens to focus on a specific field in the state.
//
// IMPORTANT: BindL is for EFFECTFUL FUNCTIONS that depend on context.Context.
// The Kleisli parameter returns a ReaderResult, which is effectful.
//
// It combines lens-based field access with monadic composition, allowing you to:
// 1. Extract a field value using the lens
// 2. Use that value in an effectful computation that may fail
// 3. Update the field with the result
//
// Type Parameters:
// - S: The state type
// - T: The type of the field to update
//
// Parameters:
// - lens: A lens that focuses on a field of type T within state S
// - f: An effectful Kleisli arrow that transforms the field value
//
// Returns:
// - An Operator that transforms ReaderResult[S] to ReaderResult[S]
//
//go:inline
func BindL[S, T any](
lens Lens[S, T],
f Kleisli[T, T],
) Operator[S, S] {
return RR.BindL(lens, WithContextK(f))
}
// LetL is a variant of Let that uses a lens to focus on a specific field in the state.
//
// IMPORTANT: LetL is for PURE FUNCTIONS (side-effect free) that don't depend on context.Context.
// The endomorphism parameter is a pure function (T -> T) with no errors or effects.
//
// It applies a pure transformation to the focused field without any effects.
//
// Type Parameters:
// - S: The state type
// - T: The type of the field to update
//
// Parameters:
// - lens: A lens that focuses on a field of type T within state S
// - f: A pure endomorphism that transforms the field value
//
// Returns:
// - An Operator that transforms ReaderResult[S] to ReaderResult[S]
//
//go:inline
func LetL[S, T any](
lens Lens[S, T],
f Endomorphism[T],
) Operator[S, S] {
return RR.LetL[context.Context](lens, f)
}
// LetToL is a variant of LetTo that uses a lens to focus on a specific field in the state.
//
// IMPORTANT: LetToL is for setting constant values. This is a PURE operation (side-effect free).
//
// It sets the focused field to a constant value.
//
// Type Parameters:
// - S: The state type
// - T: The type of the field to update
//
// Parameters:
// - lens: A lens that focuses on a field of type T within state S
// - b: The constant value to set
//
// Returns:
// - An Operator that transforms ReaderResult[S] to ReaderResult[S]
//
//go:inline
func LetToL[S, T any](
lens Lens[S, T],
b T,
) Operator[S, S] {
return RR.LetToL[context.Context](lens, b)
}
// BindReaderK binds a Reader computation (context-dependent but error-free) into the do-notation chain.
//
// IMPORTANT: This is for functions that depend on context.Context but don't return errors.
// The Reader[Context, T] is effectful because it depends on context.Context.
// Use this when you need context values but the operation cannot fail.
//
//go:inline
func BindReaderK[S1, S2, T any](
setter func(T) func(S1) S2,
f reader.Kleisli[context.Context, S1, T],
) Operator[S1, S2] {
return RR.BindReaderK(setter, f)
}
// BindEitherK binds a Result (Either) computation into the do-notation chain.
//
// IMPORTANT: This is for PURE FUNCTIONS (side-effect free) that return Result[T].
// The function (State -> Result[T]) is pure - it only depends on state, not context.
// Use this for pure error-handling logic that doesn't need context.
//
//go:inline
func BindEitherK[S1, S2, T any](
setter func(T) func(S1) S2,
f RES.Kleisli[S1, T],
) Operator[S1, S2] {
return RR.BindEitherK[context.Context](setter, f)
}
// BindResultK binds an idiomatic Go function (returning value and error) into the do-notation chain.
//
// IMPORTANT: This is for PURE FUNCTIONS (side-effect free) that return (Value, error).
// The function (State -> (Value, error)) is pure - it only depends on state, not context.
// Use this for pure computations with error handling that don't need context.
//
// For EFFECTFUL FUNCTIONS (that need context.Context), use Bind instead.
//
//go:inline
func BindResultK[S1, S2, T any](
setter func(T) func(S1) S2,
f result.Kleisli[S1, T],
) Operator[S1, S2] {
return RR.BindResultK[context.Context](setter, f)
}
// BindToReader converts a Reader computation into a ReaderResult and binds it to create an initial state.
//
// IMPORTANT: Reader[Context, T] is EFFECTFUL because it depends on context.Context.
// Use this when you have a context-dependent computation that cannot fail.
//
//go:inline
func BindToReader[
S1, T any](
setter func(T) S1,
) func(Reader[context.Context, T]) ReaderResult[S1] {
return RR.BindToReader[context.Context](setter)
}
// BindToEither converts a Result (Either) into a ReaderResult and binds it to create an initial state.
//
// IMPORTANT: Result[T] is PURE (side-effect free) - it doesn't depend on context.
// Use this to lift pure error-handling values into the ReaderResult context.
//
//go:inline
func BindToEither[
S1, T any](
setter func(T) S1,
) func(Result[T]) ReaderResult[S1] {
return RR.BindToEither[context.Context](setter)
}
// BindToResult converts an idiomatic Go tuple (value, error) into a ReaderResult and binds it to create an initial state.
//
// IMPORTANT: The (Value, error) tuple is PURE (side-effect free) - it doesn't depend on context.
// Use this to lift pure Go error-handling results into the ReaderResult context.
//
//go:inline
func BindToResult[
S1, T any](
setter func(T) S1,
) func(T, error) ReaderResult[S1] {
return RR.BindToResult[context.Context](setter)
}
// ApReaderS applies a Reader computation in applicative style, combining it with the current state.
//
// IMPORTANT: Reader[Context, T] is EFFECTFUL because it depends on context.Context.
// Use this for context-dependent operations that cannot fail.
//
//go:inline
func ApReaderS[
S1, S2, T any](
setter func(T) func(S1) S2,
fa Reader[context.Context, T],
) Operator[S1, S2] {
return RR.ApReaderS(setter, fa)
}
// ApResultS applies an idiomatic Go tuple (value, error) in applicative style.
//
// IMPORTANT: The (Value, error) tuple is PURE (side-effect free) - it doesn't depend on context.
// Use this for pure Go error-handling results.
//
//go:inline
func ApResultS[
S1, S2, T any](
setter func(T) func(S1) S2,
) func(T, error) Operator[S1, S2] {
return RR.ApResultS[context.Context](setter)
}
// ApEitherS applies a Result (Either) in applicative style, combining it with the current state.
//
// IMPORTANT: Result[T] is PURE (side-effect free) - it doesn't depend on context.
// Use this for pure error-handling values.
//
//go:inline
func ApEitherS[
S1, S2, T any](
setter func(T) func(S1) S2,
fa Result[T],
) Operator[S1, S2] {
return RR.ApEitherS[context.Context](setter, fa)
}