fp-go

repository·main·Indexed 24 days ago

https://github.com/ibm/fp-go

A comprehensive functional programming library for Go providing type-safe abstractions such as Monads, Functors, and Optics. Version 2 utilizes Go 1.24 generic type aliases for a streamlined API inspired by fp-ts. The library includes a cli package for transforming urfave/cli/v3 actions into composable CommandEffect types and a ReaderResult monad for managing effectful operations that depend on context.Context, such as HTTP requests and database transactions.

Tokens
161.8K
Snippets
306
Records
620
Agent score
80%

What's inside fp-go

  1. Overview of Common Monad Functions in fp-go/v2

    main
    The fp-go/v2 library provides a consistent set of functions across its various monad implementations. These functions are categorized by their mathematical type class origins (such as Functor, Applicative, and Monad) and their operational purpose (such as transformation, composition, or side-effect management).
  2. Explore the optics package structure

    main

    The optics package is organized into core types and specialized utilities:

    Core Optics:

    • optics/lens: Lenses for product types (structs).
    • optics/prism: Prisms for sum types (Either, Result, etc.).
    • optics/iso: Isomorphisms for equivalent types.
    • optics/optional: Optional optics for Maybe values.
    • optics/traversal: Traversals for multiple values.

    Utilities:

    • optics/builder: Builder pattern for constructing complex optics.
    • optics/codec: Type-safe encoding/decoding with validation using Type[A, O, I].

    Specialized Sub-packages: Each core package includes sub-packages for common data structures: array, either, option, and record (for maps).

  3. Performance characteristics of idiomatic/ioresult

    main

    The idiomatic/ioresult package is designed for high-performance functional programming in Go, focusing on zero-allocation core operations and sub-nanosecond execution times.

    Zero-Allocation Operations

    The following operations have zero heap allocations:

    • Construction: Of, Left, FromIO
    • Transformation: Map, Chain
    • Error Recovery: Alt, GetOrElse
    • Pipeline composition and execution of simple operations

    Low-Allocation Operations

    • Interface-based operations (using Functor, Monad, or Pointed interfaces): 1-4 allocations.
    • Applicative operations (ApFirst, ApSecond): 2-4 allocations.
    • Collection operations: $O(n)$ allocations based on input size.

    Error Path Performance

    Both the SuccessPath and ErrorPath are highly efficient with zero allocations, though the error path is slightly slower than the success path.

  4. Understand the idiomatic package structure

    main

    The idiomatic/ package provides functional programming abstractions designed to be used in a way that feels natural to Go developers. It is organized into several specialized subpackages based on the type of functional container (monad) being used:

    • idiomatic/option/: Handles optional values (presence or absence) using the Option type.
    • idiomatic/result/: Handles computations that can fail, providing either a successful value or an error.
    • idiomatic/ioresult/: Combines IO operations with error handling, managing side effects and lazy evaluation.
    • idiomatic/readerresult/: Provides a Reader context for computations that require an environment/dependency to run, returning a Result.
    • idiomatic/readerioresult/: Combines environment dependency injection with IO operations and error handling.
  5. Use the `record` package for map operations

    main

    The github.com/IBM/fp-go/v2/record package provides functional programming utilities for working with Record[K, V], which is a type alias for map[K]V. It includes operations for transforming, filtering, and reducing maps, as well as monad-like operations for composing record-based computations.

    import "github.com/IBM/fp-go/v2/record"
  6. Transform and Compose Monadic Values

    main

    Beyond core operations, fp-go/v2 provides several categories of functions to manipulate monadic structures:

    Transformation

    • Fold: Reduces a monadic structure into a single value.
    • Filter: Removes values from a monad that do not satisfy a predicate.

    Composition

    • Sequence: Converts a collection of monads into a monad containing a collection.
    • Traverse: Maps a function over a structure and then sequences the resulting monads.

    Do-Notation and Building Structures

    • Do, Bind, Let, ApS: Tools for building complex monadic structures step-by-step in a readable manner.

    Side Effects

    • ChainFirst: Executes a side effect if the previous operation succeeded.
    • Tap: Executes a side effect (like logging) while preserving the original value.

    Structure and Alternatives

    • Flatten: Collapses nested monads (e.g., M[M[A]] to M[A]).
    • Alt, OrElse, GetOrElse: Provides fallback mechanisms and recovery paths when a monad represents a failure or empty state.

    Bifunctors

    • BiMap, MapLeft: Used for transforming values in bifunctorial structures, such as transforming the error channel in a Result type.
  7. Performance comparison: Either vs Idiomatic Result

    main

    This document compares the performance of the optimized either package against the idiomatic/result package.

    Key Performance Insights

    • Zero Allocations: Both packages achieve zero heap allocations for most operations. The Either struct (24 bytes) is small enough to be returned by value on the stack, avoiding heap escape.
    • Winner by Category:
      • Constructors & Predicates: Tie (~1-2 ns/op).
      • Simple Transformations: idiomatic is 1.2x - 2x faster.
      • Monadic Operations: idiomatic is 1.2x - 2.3x faster.
      • Complex Chains: idiomatic is significantly faster (e.g., ChainFirst is 32.4x faster with zero allocations vs 72 B/op in either).
      • Extraction: idiomatic is 3x - 6x faster (e.g., GetOrElse).
    • Memory Model: The Either[E, A] struct is 24 bytes. Because it is below the Go escape threshold (~64 bytes) and returned by value, it does not cause heap allocations in normal usage.
  8. Overview of Core Monads in fp-go/v2

    main

    The core of the fp-go/v2 library is built around several fundamental monads that handle different computational contexts:

    • option: For handling optionality (presence or absence of a value).
    • either: For handling computations that can fail with an error or return a value.
    • result: For error handling.
    • io: For managing side effects.
    • iooption, ioeither, ioresult: Combinations of IO with option, either, or result contexts.

    These monads form the basis for building complex, functional pipelines in Go.

  9. What is the Effect package and when to use it

    main

    The effect package provides a high-level abstraction for managing dependencies, errors, and side effects. It is built on top of ReaderReaderIOResult and adds a layer of type-safe dependency injection.

    Core Type

    type Effect[C, A any] = readerreaderioresult.ReaderReaderIOResult[C, A]

    Where C is your custom dependency type (e.g., a Config or Services struct) and A is the successful return value.

    When to use Effect

    • Type-Safe Dependency Injection: When you need to thread typed dependencies (repositories, clients) through your application.
    • Complex Workflows: When composing multiple services and dependencies.
    • Testability: When you want to easily swap real services for mocks by providing a different dependency context.
    • Separation of Concerns: To decouple business logic from specific I/O implementations and dependency management.
  10. What is a CommandEffect?

    main

    A CommandEffect is a functional representation of a CLI command action. It is defined as:

    type CommandEffect = E.Effect[*C.Command, F.Void]

    It follows a specific execution structure that allows for deferred execution and pure logic: func(*C.Command) -> func(context.Context) -> func() -> Result[Void]

    This structure provides several benefits:

    • Composability: You can chain multiple command steps using standard functional combinators (e.g., Pipe3 and Chain).
    • Type Safety: Using Prisms with flags ensures you handle types correctly at compile-time/runtime via Option.
    • Error Handling: Errors are not thrown; they are explicitly returned as a Left value in a Result type.
    • Testability: Because the logic is encapsulated in an Effect, you can test command logic by manually providing a *C.Command and executing the resulting thunk.
  11. What is ReaderResult and how is it defined?

    main

    The ReaderResult monad is a specialized implementation of the Reader monad pattern for Go. It is designed for functions that depend on context.Context (for cancellation, deadlines, or context values) and may return an error. It allows you to write functions in a functional, declarative style while maintaining idiomatic Go error handling and context awareness.

    It is functionally equivalent to the standard Go pattern func(ctx context.Context) (A, error), but wrapped to enable monadic composition.

    type ReaderResult[A any] func(context.Context) (A, error)