builder

package
v2.1.27 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README ยถ

๐Ÿ—๏ธ Builder Pattern with fp-go

This package demonstrates a functional builder pattern using fp-go's optics library. It shows how to construct and validate objects using lenses, prisms, and codecs, separating the building phase from validation.

๐Ÿ“‹ Overview

The builder pattern here uses two key types:

  • PartialPerson ๐Ÿšง: An intermediate type with unvalidated fields (raw string and int)
  • Person โœ…: A validated type with refined fields (NonEmptyString and AdultAge)

The pattern provides two approaches for validation:

  1. Prism-based validation ๐Ÿ” (simple, no error messages)
  2. Codec-based validation ๐Ÿ“ (detailed error reporting)

๐ŸŽฏ Core Concepts

1. ๐Ÿ”ง Auto-Generated Lenses

The fp-go:Lens directive in types.go generates lens accessors for both types:

// fp-go:Lens
type PartialPerson struct {
    name string
    age  int
}

// fp-go:Lens
type Person struct {
    Name NonEmptyString
    Age  AdultAge
}

This generates:

  • partialPersonLenses with .name and .age lenses
  • personLenses with .Name and .Age lenses
2. ๐ŸŽ Exporting Setters as WithXXX Methods

The lens setters are exported as builder methods:

// WithName sets the Name field of a PartialPerson
WithName = partialPersonLenses.name.Set

// WithAge sets the Age field of a PartialPerson
WithAge = partialPersonLenses.age.Set

These return Endomorphism[*PartialPerson] functions that can be composed:

builder := F.Pipe1(
    A.From(
        WithName("Alice"),
        WithAge(25),
    ),
    allOfPartialPerson,
)
partial := builder(&PartialPerson{})

Or use the convenience function:

builder := MakePerson("Alice", 25)

๐Ÿ” Approach 1: Prism-Based Validation (No Error Messages)

Creating Validation Prisms

Define prisms that validate individual fields:

๐Ÿ’ก Tip: The optics/prism package provides many helpful out-of-the-box prisms for common validations, including:

  • NonEmptyString() - validates non-empty strings
  • ParseInt(), ParseInt64() - parses integers from strings
  • ParseFloat32(), ParseFloat64() - parses floats from strings
  • ParseBool() - parses booleans from strings
  • ParseDate(layout) - parses dates with custom layouts
  • ParseURL() - parses URLs
  • FromZero(), FromNonZero() - validates zero/non-zero values
  • RegexMatcher(), RegexNamedMatcher() - regex-based validation
  • FromOption(), FromEither(), FromResult() - extracts from monadic types
  • And many more! Check optics/prism/prisms.go for the full list.

For custom validation logic, create your own prisms:

namePrism = prism.MakePrismWithName(
    func(s string) Option[NonEmptyString] {
        if S.IsEmpty(s) {
            return option.None[NonEmptyString]()
        }
        return option.Of(NonEmptyString(s))
    },
    func(ns NonEmptyString) string {
        return string(ns)
    },
    "NonEmptyString",
)

agePrism = prism.MakePrismWithName(
    func(a int) Option[AdultAge] {
        if a < 18 {
            return option.None[AdultAge]()
        }
        return option.Of(AdultAge(a))
    },
    func(aa AdultAge) int {
        return int(aa)
    },
    "AdultAge",
)
๐ŸŽญ Creating the PersonPrism

The PersonPrism converts between a builder and a validated Person:

PersonPrism = prism.MakePrismWithName(
    buildPerson(),      // Forward: builder -> Option[*Person]
    buildEndomorphism(), // Reverse: *Person -> builder
    "Person",
)

Forward direction โžก๏ธ (buildPerson):

  1. Applies the builder to an empty PartialPerson
  2. Validates each field using field prisms
  3. Returns Some(*Person) if all validations pass, None otherwise

Reverse direction โฌ…๏ธ (buildEndomorphism):

  1. Extracts validated fields from Person
  2. Converts them back to raw types
  3. Returns a builder that reconstructs the PartialPerson
๐Ÿ’ก Usage Example
// Create a builder
builder := MakePerson("Alice", 25)

// Validate and convert to Person
maybePerson := PersonPrism.GetOption(builder)

// maybePerson is Option[*Person]
// - Some(*Person) if validation succeeds โœ…
// - None if validation fails (no error details) โŒ

๐Ÿ“ Approach 2: Codec-Based Validation (With Error Messages)

Creating Field Codecs

Convert prisms to codecs for detailed validation:

nameCodec = codec.FromRefinement(namePrism)
ageCodec = codec.FromRefinement(agePrism)
๐ŸŽฏ Creating the PersonCodec

The PersonCodec provides bidirectional transformation with validation:

func makePersonCodec() PersonCodec {
    return codec.MakeType(
        "Person",
        codec.Is[*Person](),
        makePersonValidate(),  // Validation with error reporting
        buildEndomorphism(),   // Encoding (same as prism)
    )
}

The makePersonValidate function:

  1. Applies the builder to an empty PartialPerson
  2. Validates each field using field codecs
  3. Accumulates validation errors using applicative composition ๐Ÿ“š
  4. Returns Validation[*Person] (either errors or a valid Person)
๐Ÿ’ก Usage Example
// Create a builder
builder := MakePerson("", 15) // Invalid: empty name, age < 18

// Validate with detailed errors
personCodec := makePersonCodec()
validation := personCodec.Validate(builder)

// validation is Validation[*Person]
// - Right(*Person) if validation succeeds โœ…
// - Left(ValidationErrors) with detailed error messages if validation fails โŒ

โš–๏ธ Key Differences

Feature Prism-Based ๐Ÿ” Codec-Based ๐Ÿ“
Error Messages No (returns None) โŒ Yes (returns detailed errors) โœ…
Complexity Simpler ๐ŸŸข More complex ๐ŸŸก
Use Case Simple validation Production validation with user feedback
Return Type Option[*Person] Validation[*Person]

๐Ÿ“ Pattern Summary

  1. Define types ๐Ÿ“: Create PartialPerson (unvalidated) and Person (validated)
  2. Generate lenses ๐Ÿ”ง: Use fp-go:Lens directive
  3. Export setters ๐ŸŽ: Create WithXXX methods from lens setters
  4. Create validation prisms ๐ŸŽญ: Define validation rules for each field
  5. Choose validation approach โš–๏ธ:
    • Simple ๐Ÿ”: Create a Prism for quick validation without errors
    • Detailed ๐Ÿ“: Create a Codec for validation with error reporting

โœจ Benefits

  • Type Safety ๐Ÿ›ก๏ธ: Validated types guarantee business rules at compile time
  • Composability ๐Ÿงฉ: Builders can be composed using monoid operations
  • Bidirectional โ†”๏ธ: Both prisms and codecs support encoding and decoding
  • Separation of Concerns ๐ŸŽฏ: Building and validation are separate phases
  • Functional ๐Ÿ”„: Pure functions, no mutation, easy to test

๐Ÿ“ Files

  • types.go: Type definitions and lens generation directives
  • builder.go: Prism-based builder implementation
  • codec.go: Codec-based validation implementation
  • codec_test.go: Tests demonstrating usage patterns

Documentation ยถ

Overview ยถ

Package builder demonstrates a functional builder pattern using fp-go. It shows how to construct and validate Person objects using lenses, prisms, and functional composition, separating the building phase (PartialPerson) from the validated result (Person).

Package builder demonstrates codec-based validation and encoding/decoding for Person objects using fp-go's optics and validation framework.

This file extends the builder pattern with codec functionality, enabling:

  • Bidirectional transformation between PartialPerson and Person
  • Validation with detailed error reporting
  • Type-safe encoding and decoding operations

Package builder demonstrates the builder pattern using functional programming concepts from fp-go, including validation and transformation of data structures.

Index ยถ

Constants ยถ

This section is empty.

Variables ยถ

View Source
var (

	// WithName is a builder function that sets the Name field of a PartialPerson.
	// It returns an endomorphism that can be composed with other builder operations.
	//
	// Example:
	//   builder := WithName("Alice")
	//   person := builder(&PartialPerson{})
	WithName = partialPersonLenses.name.Set

	// WithAge is a builder function that sets the Age field of a PartialPerson.
	// It returns an endomorphism that can be composed with other builder operations.
	//
	// Example:
	//   builder := WithAge(25)
	//   person := builder(&PartialPerson{})
	WithAge = partialPersonLenses.age.Set

	// PersonPrism is a prism that converts between a builder pattern (Endomorphism[*PartialPerson])
	// and a validated Person in both directions.
	//
	// Forward direction (buildPerson): Endomorphism[*PartialPerson] -> Option[*Person]
	//   - Applies the builder to an empty PartialPerson
	//   - Validates all fields using namePrism and agePrism
	//   - Returns Some(*Person) if all validations pass, None otherwise
	//
	// Reverse direction (buildEndomorphism): *Person -> Endomorphism[*PartialPerson]
	//   - Extracts validated fields from Person
	//   - Converts them back to raw types
	//   - Returns a builder that reconstructs the PartialPerson
	//
	// This enables bidirectional conversion with validation in the forward direction.
	PersonPrism = prism.MakePrismWithName(buildPerson(), buildEndomorphism(), "Person")
)

Functions ยถ

This section is empty.

Types ยถ

type AdultAge ยถ

type AdultAge uint

AdultAge is an unsigned integer type that represents a validated age that meets adult criteria (typically >= 18).

type Encode ยถ added in v2.1.21

type Encode[A, O any] = codec.Encode[A, O]

Encode represents an encoding function that transforms a value of type A into type O. It is used in codecs for the reverse direction of validation. It is an alias for codec.Encode[A, O].

type Endomorphism ยถ

type Endomorphism[A any] = endomorphism.Endomorphism[A]

Endomorphism represents a function from type A to type A. It is an alias for endomorphism.Endomorphism[A].

func MakePerson ยถ

func MakePerson(name string, age int) Endomorphism[*PartialPerson]

MakePerson creates a builder (endomorphism) that sets both name and age fields on a PartialPerson. This is a convenience function that combines WithName and WithAge into a single builder operation.

Parameters:

  • name: The name to set (will be validated later when converting to Person)
  • age: The age to set (will be validated later when converting to Person)

Returns:

An endomorphism that applies both field setters to a PartialPerson

Example:

builder := MakePerson("Alice", 25)
partial := builder(&PartialPerson{})
// partial now has Name="Alice" and Age=25

type Lens ยถ

type Lens[S, A any] = lens.Lens[S, A]

Lens represents an optic that focuses on a field of type A within a structure of type S. It provides getter and setter operations for immutable updates. It is an alias for lens.Lens[S, A].

type NonEmptyString ยถ

type NonEmptyString string

NonEmptyString is a string type that represents a validated non-empty string. It is used to ensure that string fields contain meaningful data.

type Option ยถ

type Option[A any] = option.Option[A]

Option represents an optional value of type A that may or may not be present. It is an alias for option.Option[A].

type PartialPerson ยถ

type PartialPerson struct {
	// contains filtered or unexported fields
}

PartialPerson represents a person record with unvalidated fields. This type is typically used as an intermediate representation before validation is applied to create a Person instance.

The fp-go:Lens directive generates lens functions for accessing and modifying the fields of this struct in a functional way.

fp-go:Lens

type PartialPersonLenses ยถ

type PartialPersonLenses struct {
	// contains filtered or unexported fields
}

PartialPersonLenses provides lenses for accessing fields of PartialPerson

func MakePartialPersonLenses ยถ

func MakePartialPersonLenses() PartialPersonLenses

MakePartialPersonLenses creates a new PartialPersonLenses with lenses for all fields

type PartialPersonPrisms ยถ

type PartialPersonPrisms struct {
	// contains filtered or unexported fields
}

PartialPersonPrisms provides prisms for accessing fields of PartialPerson

func MakePartialPersonPrisms ยถ

func MakePartialPersonPrisms() PartialPersonPrisms

MakePartialPersonPrisms creates a new PartialPersonPrisms with prisms for all fields

type PartialPersonRefLenses ยถ

type PartialPersonRefLenses struct {
	// contains filtered or unexported fields
}

PartialPersonRefLenses provides lenses for accessing fields of PartialPerson via a reference to PartialPerson

func MakePartialPersonRefLenses ยถ

func MakePartialPersonRefLenses() PartialPersonRefLenses

MakePartialPersonRefLenses creates a new PartialPersonRefLenses with lenses for all fields

type Person ยถ

type Person struct {
	// Name is the person's validated name, guaranteed to be non-empty.
	Name NonEmptyString

	// Age is the person's validated age, guaranteed to meet adult criteria.
	Age AdultAge
}

Person represents a person record with validated fields. All fields in this type have been validated and are guaranteed to meet specific business rules (non-empty name, adult age).

The fp-go:Lens directive generates lens functions for accessing and modifying the fields of this struct in a functional way.

fp-go:Lens

type PersonCodec ยถ added in v2.1.21

type PersonCodec = Type[*Person, Endomorphism[*PartialPerson], Endomorphism[*PartialPerson]]

PersonCodec is a codec type that handles bidirectional transformation between Person and PartialPerson using endomorphisms.

Type parameters:

  • A: *Person - The validated target type
  • O: Endomorphism[*PartialPerson] - The output encoding type (builder)
  • I: Endomorphism[*PartialPerson] - The input decoding type (builder)

This codec enables:

  • Validation: Converting a PartialPerson builder to a validated Person
  • Encoding: Converting a Person back to a PartialPerson builder

type PersonLenses ยถ

type PersonLenses struct {
	// mandatory fields
	Name __lens.Lens[Person, NonEmptyString]
	Age  __lens.Lens[Person, AdultAge]
	// optional fields
	NameO __lens_option.LensO[Person, NonEmptyString]
	AgeO  __lens_option.LensO[Person, AdultAge]
}

PersonLenses provides lenses for accessing fields of Person

func MakePersonLenses ยถ

func MakePersonLenses() PersonLenses

MakePersonLenses creates a new PersonLenses with lenses for all fields

type PersonPrisms ยถ

type PersonPrisms struct {
	Name __prism.Prism[Person, NonEmptyString]
	Age  __prism.Prism[Person, AdultAge]
}

PersonPrisms provides prisms for accessing fields of Person

func MakePersonPrisms ยถ

func MakePersonPrisms() PersonPrisms

MakePersonPrisms creates a new PersonPrisms with prisms for all fields

type PersonRefLenses ยถ

type PersonRefLenses struct {
	// mandatory fields
	Name __lens.Lens[*Person, NonEmptyString]
	Age  __lens.Lens[*Person, AdultAge]
	// optional fields
	NameO __lens_option.LensO[*Person, NonEmptyString]
	AgeO  __lens_option.LensO[*Person, AdultAge]
	// prisms
	NameP __prism.Prism[*Person, NonEmptyString]
	AgeP  __prism.Prism[*Person, AdultAge]
}

PersonRefLenses provides lenses for accessing fields of Person via a reference to Person

func MakePersonRefLenses ยถ

func MakePersonRefLenses() PersonRefLenses

MakePersonRefLenses creates a new PersonRefLenses with lenses for all fields

type Prism ยถ

type Prism[S, A any] = prism.Prism[S, A]

Prism represents an optic that focuses on a subset of values of type S that can be converted to type A. It provides bidirectional transformation with validation. It is an alias for prism.Prism[S, A].

type Reader ยถ

type Reader[R, A any] = reader.Reader[R, A]

Reader represents a computation that depends on an environment R and produces a value of type A. It is an alias for reader.Reader[R, A].

type ReaderOption ยถ

type ReaderOption[R, A any] = readeroption.ReaderOption[R, A]

ReaderOption represents a computation that depends on an environment R and produces an optional value of type A. It is an alias for readeroption.ReaderOption[R, A].

type Result ยถ

type Result[A any] = result.Result[A]

Result represents a computation that may succeed with a value of type A or fail with an error. It is an alias for result.Result[A].

type Type ยถ added in v2.1.21

type Type[A, O, I any] = codec.Type[A, O, I]

Type represents a codec that handles bidirectional transformation between types. A: The validated target type O: The output encoding type I: The input decoding type It is an alias for codec.Type[A, O, I].

type Validate ยถ added in v2.1.21

type Validate[I, A any] = validate.Validate[I, A]

Validate represents a validation function that transforms input I into a validated result A. It returns a Validation that contains either the validated value or validation errors. It is an alias for validate.Validate[I, A].

type Validation ยถ added in v2.1.21

type Validation[A any] = validation.Validation[A]

Validation represents the result of a validation operation. It contains either a validated value of type A (Right) or validation errors (Left). It is an alias for validation.Validation[A].

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL