option

package
v2.2.21 Latest Latest
Warning

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

Go to latest
Published: Feb 24, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package option provides isomorphisms for working with Option types.

Overview

This package offers utilities to convert between regular values and Option-wrapped values, particularly useful for handling zero values and optional data. It provides isomorphisms that treat certain values (like zero values) as representing absence, mapping them to None, while other values map to Some.

Core Functionality

The main function in this package is FromZero, which creates an isomorphism between a comparable type T and Option[T], treating the zero value as None.

FromZero Isomorphism

FromZero creates a bidirectional transformation where:

  • Forward (Get): T → Option[T]
  • Zero value → None
  • Non-zero value → Some(value)
  • Reverse (ReverseGet): Option[T] → T
  • None → Zero value
  • Some(value) → value

Basic Usage

Working with integers:

import (
	"github.com/IBM/fp-go/v2/optics/iso/option"
	O "github.com/IBM/fp-go/v2/option"
)

isoInt := option.FromZero[int]()

// Convert zero to None
opt := isoInt.Get(0) // None[int]

// Convert non-zero to Some
opt = isoInt.Get(42) // Some(42)

// Convert None to zero
val := isoInt.ReverseGet(O.None[int]()) // 0

// Convert Some to value
val = isoInt.ReverseGet(O.Some(42)) // 42

Use Cases

## Database Nullable Columns

Convert between database NULL and Go zero values:

type User struct {
	ID       int
	Name     string
	Age      *int  // NULL in database
	Email    *string
}

ageIso := option.FromZero[*int]()

// Reading from database
var dbAge *int = nil
optAge := ageIso.Get(dbAge) // None[*int]

// Writing to database
userAge := 25
dbAge = ageIso.ReverseGet(O.Some(&userAge)) // &25

## Configuration with Defaults

Handle optional configuration values:

type Config struct {
	Port    int
	Timeout int
	MaxConn int
}

portIso := option.FromZero[int]()

// Use zero as "not configured"
config := Config{Port: 0, Timeout: 30, MaxConn: 100}
portOpt := portIso.Get(config.Port) // None[int] (use default)

// Set explicit value
config.Port = portIso.ReverseGet(O.Some(8080)) // 8080

## API Response Handling

Work with APIs that use zero values to indicate absence:

type APIResponse struct {
	UserID   int     // 0 means not set
	Score    float64 // 0.0 means not available
	Message  string  // "" means no message
}

userIDIso := option.FromZero[int]()
scoreIso := option.FromZero[float64]()
messageIso := option.FromZero[string]()

response := APIResponse{UserID: 0, Score: 0.0, Message: ""}

userID := userIDIso.Get(response.UserID)     // None[int]
score := scoreIso.Get(response.Score)        // None[float64]
message := messageIso.Get(response.Message)  // None[string]

## Validation Logic

Simplify required vs optional field validation:

import S "github.com/IBM/fp-go/v2/string"

type FormData struct {
	Name     string // Required
	Email    string // Required
	Phone    string // Optional (empty = not provided)
	Comments string // Optional
}

phoneIso := option.FromZero[string]()
commentsIso := option.FromZero[string]()

form := FormData{
	Name:     "Alice",
	Email:    "alice@example.com",
	Phone:    "",
	Comments: "",
}

// Check optional fields
phone := phoneIso.Get(form.Phone)       // None[string]
comments := commentsIso.Get(form.Comments) // None[string]

// Validate: required fields must be non-empty
if S.IsEmpty(form.Name) || S.IsEmpty(form.Email) {
	// Validation error
}

Working with Different Types

## Strings

strIso := option.FromZero[string]()

opt := strIso.Get("")        // None[string]
opt = strIso.Get("hello")    // Some("hello")

val := strIso.ReverseGet(O.None[string]())      // ""
val = strIso.ReverseGet(O.Some("world"))        // "world"

## Pointers

ptrIso := option.FromZero[*int]()

opt := ptrIso.Get(nil)       // None[*int]
num := 42
opt = ptrIso.Get(&num)       // Some(&num)

val := ptrIso.ReverseGet(O.None[*int]())  // nil
val = ptrIso.ReverseGet(O.Some(&num))     // &num

## Floating Point Numbers

floatIso := option.FromZero[float64]()

opt := floatIso.Get(0.0)     // None[float64]
opt = floatIso.Get(3.14)     // Some(3.14)

val := floatIso.ReverseGet(O.None[float64]())  // 0.0
val = floatIso.ReverseGet(O.Some(2.71))        // 2.71

## Booleans

boolIso := option.FromZero[bool]()

opt := boolIso.Get(false)    // None[bool]
opt = boolIso.Get(true)      // Some(true)

val := boolIso.ReverseGet(O.None[bool]())   // false
val = boolIso.ReverseGet(O.Some(true))      // true

Composition with Other Optics

Combine with lenses for nested structures:

import (
	L "github.com/IBM/fp-go/v2/optics/lens"
	I "github.com/IBM/fp-go/v2/optics/iso"
)

type Settings struct {
	Volume int // 0 means muted
}

volumeLens := L.MakeLens(
	func(s Settings) int { return s.Volume },
	func(s Settings, v int) Settings {
		s.Volume = v
		return s
	},
)

volumeIso := option.FromZero[int]()

// Compose lens with iso
volumeOptLens := F.Pipe1(
	volumeLens,
	L.IMap[Settings](volumeIso.Get, volumeIso.ReverseGet),
)

settings := Settings{Volume: 0}
vol := volumeOptLens.Get(settings) // None[int] (muted)

// Set volume
updated := volumeOptLens.Set(O.Some(75))(settings)
// updated.Volume == 75

Isomorphism Laws

FromZero satisfies the isomorphism round-trip laws:

1. **ReverseGet(Get(t)) == t** for all t: T

isoInt := option.FromZero[int]()
value := 42
result := isoInt.ReverseGet(isoInt.Get(value))
// result == 42

2. **Get(ReverseGet(opt)) == opt** for all opt: Option[T]

isoInt := option.FromZero[int]()
opt := O.Some(42)
result := isoInt.Get(isoInt.ReverseGet(opt))
// result == Some(42)

These laws ensure that the transformation is truly reversible with no information loss.

Performance Considerations

The FromZero isomorphism is very efficient:

  • No allocations for the iso structure itself
  • Simple equality comparison for zero check
  • Direct value unwrapping for ReverseGet
  • No reflection or runtime type assertions

Type Safety

The isomorphism is fully type-safe:

  • Compile-time type checking ensures T is comparable
  • Generic type parameters prevent type mismatches
  • No runtime type assertions needed
  • The compiler enforces correct usage

Limitations

The FromZero isomorphism has some limitations to be aware of:

1. **Zero Value Ambiguity**: Cannot distinguish between "intentionally zero" and "absent"

  • For int: 0 always maps to None, even if 0 is a valid value
  • For string: "" always maps to None, even if empty string is valid
  • Solution: Use a different representation (e.g., pointers) if zero is meaningful

2. **Comparable Constraint**: Only works with comparable types

  • Cannot use with slices, maps, or functions
  • Cannot use with structs containing non-comparable fields
  • Solution: Use pointers to such types, or custom isomorphisms

3. **Boolean Limitation**: false always maps to None

  • Cannot represent "explicitly false" vs "not set"
  • Solution: Use *bool or a custom type if this distinction matters
  • github.com/IBM/fp-go/v2/optics/iso: Core isomorphism functionality
  • github.com/IBM/fp-go/v2/option: Option type and operations
  • github.com/IBM/fp-go/v2/optics/lens: Lenses for focused access
  • github.com/IBM/fp-go/v2/optics/lens/option: Lenses for optional values

See Also

For more information on isomorphisms and optics:

  • optics/iso package documentation
  • optics package overview
  • option package documentation

Package option provides isomorphisms for working with Option types. It offers utilities to convert between regular values and Option-wrapped values, particularly useful for handling zero values and optional data.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FromZero

func FromZero[T comparable]() iso.Iso[T, option.Option[T]]

FromZero creates an isomorphism between a comparable type T and Option[T]. The isomorphism treats the zero value of T as None and non-zero values as Some.

This is particularly useful for types where the zero value has special meaning (e.g., 0 for numbers, "" for strings, nil for pointers) and you want to represent the absence of a meaningful value using Option.

Type Parameters:

  • T: A comparable type (must support == and != operators)

Returns:

  • An Iso[T, Option[T]] where:
  • Get: Converts T to Option[T] (zero value → None, non-zero → Some)
  • ReverseGet: Converts Option[T] to T (None → zero value, Some → unwrapped value)

Behavior:

  • Get direction: If the value equals the zero value of T, returns None; otherwise returns Some(value)
  • ReverseGet direction: If the Option is None, returns the zero value; otherwise returns the unwrapped value

Example with integers:

isoInt := FromZero[int]()
opt := isoInt.Get(0)        // None (0 is the zero value)
opt = isoInt.Get(42)        // Some(42)
val := isoInt.ReverseGet(option.None[int]())  // 0
val = isoInt.ReverseGet(option.Some(42))      // 42

Example with strings:

isoStr := FromZero[string]()
opt := isoStr.Get("")       // None ("" is the zero value)
opt = isoStr.Get("hello")   // Some("hello")
val := isoStr.ReverseGet(option.None[string]())  // ""
val = isoStr.ReverseGet(option.Some("world"))    // "world"

Example with pointers:

isoPtr := FromZero[*int]()
opt := isoPtr.Get(nil)      // None (nil is the zero value)
num := 42
opt = isoPtr.Get(&num)      // Some(&num)

Use cases:

  • Converting between database nullable columns and Go types
  • Handling optional configuration values with defaults
  • Working with APIs that use zero values to indicate absence
  • Simplifying validation logic for required vs optional fields

Note: This isomorphism satisfies the round-trip laws:

  • ReverseGet(Get(t)) == t for all t: T
  • Get(ReverseGet(opt)) == opt for all opt: Option[T]

Types

This section is empty.

Jump to

Keyboard shortcuts

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