Skip to main content
Version: v2.2.82 (latest)
Reference · Core Type

ReaderOption

Dependency injection with optional values. ReaderOption[C, A] combines Reader context with Option for computations that require dependencies and may not return a result.

01

Overview

ReaderOption represents a computation that requires context and may not return a value:

  • Reader: Dependency injection pattern
  • Option: Optional result (Some/None)
type_definition.go
package readeroption

// ReaderOption is Reader specialized for optional results
type ReaderOption[C, A any] = Reader[C, Option[A]]
// Which expands to: func(C) Option[A]

When to Use

Use CaseExample
Optional results with dependenciesCache lookups, database queries
ConfigurationOptional settings from context
Not an errorAbsence is a valid outcome
Composable lookupsChain optional operations with context
02

Core API

Constructors

FunctionSignatureDescription
Somefunc Some[C, A any](value A) ReaderOption[C, A]Create with value
Nonefunc None[C, A any]() ReaderOption[C, A]Create empty
Offunc Of[C, A any](value A) ReaderOption[C, A]Alias for Some
FromReaderfunc FromReader[C, A any](r Reader[C, A]) ReaderOption[C, A]Lift Reader to ReaderOption

Transformations

FunctionSignatureDescription
Mapfunc Map[C, A, B any](f func(A) B) func(ReaderOption[C, A]) ReaderOption[C, B]Transform value if present
Chainfunc Chain[C, A, B any](f func(A) ReaderOption[C, B]) func(ReaderOption[C, A]) ReaderOption[C, B]FlatMap - sequence operations
Apfunc Ap[C, A, B any](fa ReaderOption[C, A]) func(ReaderOption[C, func(A) B]) ReaderOption[C, B]Apply wrapped function

Extraction

FunctionSignatureDescription
GetOrElsefunc GetOrElse[C, A any](f func() A) func(ReaderOption[C, A]) Reader[C, A]Extract with default
Foldfunc Fold[C, A, B any](onNone func() B, onSome func(A) B) func(ReaderOption[C, A]) Reader[C, B]Pattern match both cases

Testing

FunctionSignatureDescription
IsSomefunc IsSome[C, A any](ReaderOption[C, A]) Reader[C, bool]Test for value
IsNonefunc IsNone[C, A any](ReaderOption[C, A]) Reader[C, bool]Test for absence
03

Usage Examples

Basic Operations

basic.go
package main

import (
  "github.com/IBM/fp-go/v2/readeroption"
  "github.com/IBM/fp-go/v2/option"
)

type Dependencies struct {
  Cache *Cache
  DB    *sql.DB
}

func FindInCache(key string) readeroption.ReaderOption[Dependencies, Data] {
  return func(deps Dependencies) option.Option[Data] {
      if val, ok := deps.Cache.Get(key); ok {
          return option.Some(val)
      }
      return option.None[Data]()
  }
}

func main() {
  deps := Dependencies{Cache: NewCache()}
  
  // Execute with dependencies
  result := FindInCache("key")(deps)
  
  // Check result
  if option.IsSome(result) {
      fmt.Println("Found in cache")
  }
}

Optional Configuration

config.go
package main

import (
  RO "github.com/IBM/fp-go/v2/readeroption"
  O "github.com/IBM/fp-go/v2/option"
)

type Config struct {
  Settings map[string]string
}

func GetSetting(key string) RO.ReaderOption[Config, string] {
  return func(cfg Config) O.Option[string] {
      if val, ok := cfg.Settings[key]; ok {
          return O.Some(val)
      }
      return O.None[string]()
  }
}

func main() {
  cfg := Config{Settings: map[string]string{"theme": "dark"}}
  
  // With fallback
  theme := RO.GetOrElse(func() string {
      return "light"
  })(GetSetting("theme"))(cfg)
  
  fmt.Println(theme) // "dark"
}

Chaining Lookups

chaining.go
package main

import (
  RO "github.com/IBM/fp-go/v2/readeroption"
  F "github.com/IBM/fp-go/v2/function"
)

type Dependencies struct {
  Cache *Cache
  DB    *sql.DB
}

func FindInCache(id string) RO.ReaderOption[Dependencies, User] {
  return func(deps Dependencies) option.Option[User] {
      if user, ok := deps.Cache.Get(id); ok {
          return option.Some(user)
      }
      return option.None[User]()
  }
}

func FindInDB(id string) RO.ReaderOption[Dependencies, User] {
  return func(deps Dependencies) option.Option[User] {
      user, err := deps.DB.Query(id)
      if err != nil {
          return option.None[User]()
      }
      return option.Some(user)
  }
}

func FindUser(id string) RO.ReaderOption[Dependencies, User] {
  return F.Pipe2(
      FindInCache(id),
      RO.Alt(func() RO.ReaderOption[Dependencies, User] {
          return FindInDB(id)
      }),
  )
}

Validation with Context

validation.go
package main

import (
  RO "github.com/IBM/fp-go/v2/readeroption"
  O "github.com/IBM/fp-go/v2/option"
)

type ValidationContext struct {
  AllowedDomains []string
}

func ValidateEmail(email string) RO.ReaderOption[ValidationContext, string] {
  return func(ctx ValidationContext) O.Option[string] {
      domain := extractDomain(email)
      for _, allowed := range ctx.AllowedDomains {
          if domain == allowed {
              return O.Some(email)
          }
      }
      return O.None[string]()
  }
}

func main() {
  ctx := ValidationContext{
      AllowedDomains: []string{"example.com", "test.com"},
  }
  
  result := ValidateEmail("user@example.com")(ctx)
  // Some("user@example.com")
  
  result = ValidateEmail("user@invalid.com")(ctx)
  // None
}
04

Common Patterns

Pattern 1: Fallback Chain

fallback.go
package main

import (
  RO "github.com/IBM/fp-go/v2/readeroption"
  F "github.com/IBM/fp-go/v2/function"
)

// Try multiple sources in order
func GetConfig(key string) RO.ReaderOption[Dependencies, string] {
  return F.Pipe3(
      GetFromEnv(key),
      RO.Alt(func() RO.ReaderOption[Dependencies, string] {
          return GetFromFile(key)
      }),
      RO.Alt(func() RO.ReaderOption[Dependencies, string] {
          return GetFromDefaults(key)
      }),
  )
}

Pattern 2: Conditional Execution

conditional.go
package main

import (
  RO "github.com/IBM/fp-go/v2/readeroption"
  O "github.com/IBM/fp-go/v2/option"
)

func GetFeature(name string) RO.ReaderOption[AppContext, Feature] {
  return func(ctx AppContext) O.Option[Feature] {
      if !ctx.FeatureFlags.IsEnabled(name) {
          return O.None[Feature]()
      }
      return O.Some(ctx.Features[name])
  }
}