// Copyright (c) 2024 - 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 statereaderioresult provides a functional programming abstraction that combines // four powerful concepts: State, Reader, IO, and Result monads, specialized for Go's context.Context. // // # StateReaderIOResult // // StateReaderIOResult[S, A] represents a computation that: // - Manages state of type S (State) // - Depends on a [context.Context] (Reader) // - Performs side effects (IO) // - Can fail with an [error] or succeed with a value of type A (Result) // // This is a specialization of StateReaderIOEither with: // - Context type fixed to [context.Context] // - Error type fixed to [error] // // This is particularly useful for: // - Stateful computations with dependency injection using Go contexts // - Error handling in effectful computations with state // - Composing operations that need access to context, manage state, and can fail // - Working with Go's standard context patterns (cancellation, deadlines, values) // // # Core Operations // // Construction: // - Of/Right: Create a successful computation with a value // - Left: Create a failed computation with an error // - FromState: Lift a State into StateReaderIOResult // - FromIO: Lift an IO into StateReaderIOResult // - FromResult: Lift a Result into StateReaderIOResult // - FromIOResult: Lift an IOResult into StateReaderIOResult // - FromReaderIOResult: Lift a ReaderIOResult into StateReaderIOResult // // Transformation: // - Map: Transform the success value // - Chain: Sequence dependent computations (monadic bind) // - Flatten: Flatten nested StateReaderIOResult // // Combination: // - Ap: Apply a function in a context to a value in a context // // Context Access: // - Asks: Get a value derived from the context // - Local: Run a computation with a modified context // // Kleisli Arrows: // - FromResultK: Lift a Result-returning function to a Kleisli arrow // - FromIOK: Lift an IO-returning function to a Kleisli arrow // - FromIOResultK: Lift an IOResult-returning function to a Kleisli arrow // - FromReaderIOResultK: Lift a ReaderIOResult-returning function to a Kleisli arrow // - ChainResultK: Chain with a Result-returning function // - ChainIOResultK: Chain with an IOResult-returning function // - ChainReaderIOResultK: Chain with a ReaderIOResult-returning function // // Do Notation (Monadic Composition): // - Do: Start a do-notation chain // - Bind: Bind a value from a computation // - BindTo: Bind a value to a simple constructor // - Let: Compute a derived value // - LetTo: Set a constant value // - ApS: Apply in sequence (for applicative composition) // - BindL/ApSL/LetL/LetToL: Lens-based variants for working with nested structures // // # Example Usage // // type AppState struct { // RequestCount int // LastError error // } // // // A computation that manages state, depends on context, performs IO, and can fail // func processRequest(data string) statereaderioresult.StateReaderIOResult[AppState, string] { // return func(state AppState) readerioresult.ReaderIOResult[pair.Pair[AppState, string]] { // return func(ctx context.Context) ioresult.IOResult[pair.Pair[AppState, string]] { // return func() result.Result[pair.Pair[AppState, string]] { // // Check context for cancellation // if ctx.Err() != nil { // return result.Error[pair.Pair[AppState, string]](ctx.Err()) // } // // Update state // newState := AppState{RequestCount: state.RequestCount + 1} // // Perform IO operations // return result.Of(pair.MakePair(newState, "processed: " + data)) // } // } // } // } // // // Compose operations using do-notation // result := function.Pipe3( // statereaderioresult.Do[AppState](State{}), // statereaderioresult.Bind( // func(result string) func(State) State { return func(s State) State { return State{result: result} } }, // func(s State) statereaderioresult.StateReaderIOResult[AppState, string] { // return processRequest(s.input) // }, // ), // statereaderioresult.Map[AppState](func(s State) string { return s.result }), // ) // // // Execute with initial state and context // initialState := AppState{RequestCount: 0} // ctx := context.Background() // outcome := result(initialState)(ctx)() // Returns result.Result[pair.Pair[AppState, string]] // // # Context Integration // // This package is designed to work seamlessly with Go's context.Context: // // // Using context values // getUserID := statereaderioresult.Asks[AppState, string](func(ctx context.Context) statereaderioresult.StateReaderIOResult[AppState, string] { // userID, ok := ctx.Value("userID").(string) // if !ok { // return statereaderioresult.Left[AppState, string](errors.New("missing userID")) // } // return statereaderioresult.Of[AppState](userID) // }) // // // Using context cancellation // withTimeout := statereaderioresult.Local[AppState, string](func(ctx context.Context) context.Context { // ctx, _ = context.WithTimeout(ctx, 5*time.Second) // return ctx // }) // // # Monad Laws // // StateReaderIOResult satisfies the monad laws: // - Left Identity: Of(a) >>= f ≡ f(a) // - Right Identity: m >>= Of ≡ m // - Associativity: (m >>= f) >>= g ≡ m >>= (x => f(x) >>= g) // // These laws are verified in the testing subpackage. package statereaderioresult