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

Lazy

Deferred computation that produces a value. Lazy[A] emphasizes memoization for expensive, pure computations.

01

Overview

Lazy is identical to IO in structure but emphasizes pure, memoizable computations:

type_definition.go
package lazy

// Lazy is a deferred computation
type Lazy[A any] = func() A

Lazy vs IO

LazyIO
Pure computationsSide effects
Emphasizes memoizationEmphasizes laziness
Expensive calculationsTime-based operations
Circular dependenciesRandom values, logging
02

Core API

Constructors

FunctionSignatureDescription
Offunc Of[A any](f func() A) Lazy[A]Create lazy computation

Transformations

FunctionSignatureDescription
Mapfunc Map[A, B any](f func(A) B) func(Lazy[A]) Lazy[B]Transform result
Chainfunc Chain[A, B any](f func(A) Lazy[B]) func(Lazy[A]) Lazy[B]Sequence operations
Flattenfunc Flatten[A any](Lazy[Lazy[A]]) Lazy[A]Unwrap nested Lazy

Combining

FunctionSignatureDescription
Apfunc Ap[A, B any](fa Lazy[A]) func(Lazy[func(A) B]) Lazy[B]Apply wrapped function
03

Usage Examples

Basic Lazy Evaluation

basic.go
package main

import (
  "fmt"
  L "github.com/IBM/fp-go/v2/lazy"
)

func main() {
  // Defer expensive computation
  expensive := L.Of(func() int {
      fmt.Println("Computing...")
      // Expensive operation
      return 42
  })
  
  fmt.Println("Lazy value created")
  
  // Only computed when called
  result := expensive()  // Prints "Computing..." then returns 42
}

Memoization Pattern

memoization.go
package main

import (
  "sync"
  L "github.com/IBM/fp-go/v2/lazy"
)

var once sync.Once
var cached Data

func GetCachedData() L.Lazy[Data] {
  return func() Data {
      once.Do(func() {
          fmt.Println("Computing expensive data...")
          cached = computeExpensiveData()
      })
      return cached
  }
}

func main() {
  lazyData := GetCachedData()
  
  // First call computes
  data1 := lazyData()  // Prints "Computing expensive data..."
  
  // Subsequent calls use cache
  data2 := lazyData()  // No output, uses cached value
  data3 := lazyData()  // No output, uses cached value
}

Transformations

transformations.go
package main

import (
  L "github.com/IBM/fp-go/v2/lazy"
  F "github.com/IBM/fp-go/v2/function"
)

func main() {
  // Map: transform result
  doubled := F.Pipe1(
      L.Of(func() int { return 21 }),
      L.Map(func(n int) int { return n * 2 }),
  )
  
  result := doubled()  // 42
  
  // Chain: sequence operations
  result := F.Pipe1(
      L.Of(func() int { return 10 }),
      L.Chain(func(n int) L.Lazy[string] {
          return L.Of(func() string {
              return fmt.Sprintf("Value: %d", n)
          })
      }),
  )
  
  output := result()  // "Value: 10"
}

Circular Dependencies

circular.go
package main

import (
  L "github.com/IBM/fp-go/v2/lazy"
)

type Node struct {
  Value int
  Next  L.Lazy[*Node]
}

func createCircularList() *Node {
  var node1, node2, node3 *Node
  
  node1 = &Node{
      Value: 1,
      Next: L.Of(func() *Node {
          return node2  // Forward reference
      }),
  }
  
  node2 = &Node{
      Value: 2,
      Next: L.Of(func() *Node {
          return node3
      }),
  }
  
  node3 = &Node{
      Value: 3,
      Next: L.Of(func() *Node {
          return node1  // Circular reference
      }),
  }
  
  return node1
}

func main() {
  list := createCircularList()
  
  // Navigate the circular list
  current := list
  for i := 0; i < 5; i++ {
      fmt.Println(current.Value)
      current = current.Next()
  }
  // Output: 1 2 3 1 2
}
04

Common Patterns

Pattern 1: Expensive Initialization

expensive_init.go
package main

import (
  "sync"
  L "github.com/IBM/fp-go/v2/lazy"
)

type Service struct {
  config L.Lazy[Config]
  db     L.Lazy[*sql.DB]
}

func NewService() *Service {
  var configOnce, dbOnce sync.Once
  var cachedConfig Config
  var cachedDB *sql.DB
  
  return &Service{
      config: func() Config {
          configOnce.Do(func() {
              cachedConfig = loadConfig()  // Expensive
          })
          return cachedConfig
      },
      db: func() *sql.DB {
          dbOnce.Do(func() {
              cachedDB = connectDB()  // Expensive
          })
          return cachedDB
      },
  }
}

func (s *Service) GetUser(id string) User {
  // Config and DB only loaded when first accessed
  cfg := s.config()
  db := s.db()
  return queryUser(db, id)
}

Pattern 2: Conditional Computation

conditional.go
package main

import (
  L "github.com/IBM/fp-go/v2/lazy"
)

func processData(condition bool) Result {
  // Expensive computation only created if needed
  expensiveResult := L.Of(func() Data {
      return computeExpensiveData()
  })
  
  if condition {
      // Only computed if condition is true
      data := expensiveResult()
      return process(data)
  }
  
  // Never computed if condition is false
  return defaultResult()
}

When to Use Lazy

Use Lazy WhenUse IO When
Pure, expensive computationsSide effects
Need memoizationTime-based operations
Breaking circular dependenciesRandom values
Conditional expensive operationsLogging, file I/O