mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-19 23:42:05 +02:00
114 lines
4.1 KiB
Go
114 lines
4.1 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"
|
|
|
|
F "github.com/IBM/fp-go/v2/function"
|
|
"github.com/IBM/fp-go/v2/idiomatic/result"
|
|
)
|
|
|
|
// WithContext wraps an existing ReaderResult and performs a context check for cancellation before delegating
|
|
// to the underlying computation.
|
|
//
|
|
// If the context has been cancelled (ctx.Err() != nil), it immediately returns an error containing the
|
|
// cancellation cause without executing the wrapped computation. Otherwise, it delegates to the original
|
|
// ReaderResult.
|
|
//
|
|
// This is useful for adding cancellation checks to computations that may not check the context themselves,
|
|
// ensuring that cancelled operations fail fast.
|
|
//
|
|
// Example:
|
|
//
|
|
// // A computation that might take a long time
|
|
// slowComputation := func(ctx context.Context) (int, error) {
|
|
// time.Sleep(5 * time.Second)
|
|
// return 42, nil
|
|
// }
|
|
//
|
|
// // Wrap it to check for cancellation before execution
|
|
// safeSlow := readerresult.WithContext(slowComputation)
|
|
//
|
|
// // If context is already cancelled, this returns immediately
|
|
// ctx, cancel := context.WithCancel(context.Background())
|
|
// cancel() // Cancel immediately
|
|
// result, err := safeSlow(ctx) // Returns error immediately without sleeping
|
|
func WithContext[A any](ma ReaderResult[A]) ReaderResult[A] {
|
|
return func(ctx context.Context) (A, error) {
|
|
if ctx.Err() != nil {
|
|
return result.Left[A](context.Cause(ctx))
|
|
}
|
|
return ma(ctx)
|
|
}
|
|
}
|
|
|
|
// WithContextK wraps a Kleisli arrow (a function that returns a ReaderResult) with context cancellation checking.
|
|
//
|
|
// This is the Kleisli arrow version of WithContext. It takes a function A -> ReaderResult[B] and returns
|
|
// a new function that performs the same transformation but with an added context cancellation check before
|
|
// executing the resulting ReaderResult.
|
|
//
|
|
// A Kleisli arrow is a function that takes a value and returns a monadic computation. In this case,
|
|
// Kleisli[A, B] = func(A) ReaderResult[B], which represents a function from A to a context-dependent
|
|
// computation that may fail.
|
|
//
|
|
// WithContextK is particularly useful when composing operations with Chain/Bind, as it ensures that
|
|
// each step in the composition checks for cancellation before proceeding.
|
|
//
|
|
// Parameters:
|
|
// - f: A Kleisli arrow (function from A to ReaderResult[B])
|
|
//
|
|
// Returns:
|
|
// - A new Kleisli arrow that wraps the result of f with context cancellation checking
|
|
//
|
|
// Example:
|
|
//
|
|
// // A function that fetches user details
|
|
// fetchUserDetails := func(userID int) readerresult.ReaderResult[UserDetails] {
|
|
// return func(ctx context.Context) (UserDetails, error) {
|
|
// // Fetch from database...
|
|
// return details, nil
|
|
// }
|
|
// }
|
|
//
|
|
// // Wrap it to ensure cancellation is checked before each execution
|
|
// safeFetchDetails := readerresult.WithContextK(fetchUserDetails)
|
|
//
|
|
// // Use in a composition chain
|
|
// pipeline := F.Pipe2(
|
|
// getUser(42),
|
|
// readerresult.Chain(safeFetchDetails), // Checks cancellation before fetching details
|
|
// )
|
|
//
|
|
// // If context is cancelled between getUser and fetchUserDetails,
|
|
// // the details fetch will not execute
|
|
// ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
// defer cancel()
|
|
// result, err := pipeline(ctx)
|
|
//
|
|
// Use Cases:
|
|
// - Adding cancellation checks to composed operations
|
|
// - Ensuring long-running pipelines respect context cancellation
|
|
// - Wrapping third-party functions that don't check context themselves
|
|
// - Creating fail-fast behavior in complex operation chains
|
|
func WithContextK[A, B any](f Kleisli[A, B]) Kleisli[A, B] {
|
|
return F.Flow2(
|
|
f,
|
|
WithContext,
|
|
)
|
|
}
|