effect

package
v2.2.6 Latest Latest
Warning

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

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

Documentation

Overview

Package effect provides a functional effect system for managing side effects in Go.

Overview

The effect package is a high-level abstraction for composing effectful computations that may fail, require dependencies (context), and perform I/O operations. It is built on top of ReaderReaderIOResult, providing a clean API for dependency injection and error handling.

Naming Conventions

The naming conventions in this package are modeled after effect-ts (https://effect.website/), a popular TypeScript library for functional effect systems. This alignment helps developers familiar with effect-ts to quickly understand and use this Go implementation.

Core Type

The central type is Effect[C, A], which represents:

  • C: The context/dependency type required by the effect
  • A: The success value type produced by the effect

An Effect can:

  • Succeed with a value of type A
  • Fail with an error
  • Require a context of type C
  • Perform I/O operations

Basic Operations

Creating Effects:

// Create a successful effect
effect.Succeed[MyContext, string]("hello")

// Create a failed effect
effect.Fail[MyContext, string](errors.New("failed"))

// Lift a pure value into an effect
effect.Of[MyContext, int](42)

Transforming Effects:

// Map over the success value
effect.Map[MyContext](func(x int) string {
	return strconv.Itoa(x)
})

// Chain effects together (flatMap)
effect.Chain[MyContext](func(x int) Effect[MyContext, string] {
	return effect.Succeed[MyContext, string](strconv.Itoa(x))
})

// Tap into an effect without changing its value
effect.Tap[MyContext](func(x int) Effect[MyContext, any] {
	return effect.Succeed[MyContext, any](fmt.Println(x))
})

Dependency Injection

Effects can access their required context:

// Transform the context before passing it to an effect
effect.Local[OuterCtx, InnerCtx](func(outer OuterCtx) InnerCtx {
	return outer.Inner
})

// Provide a context to run an effect
effect.Provide[MyContext, string](myContext)

Do Notation

The package provides "do notation" for composing effects in a sequential, imperative style:

type State struct {
	X int
	Y string
}

result := effect.Do[MyContext](State{}).
	Bind(func(y string) func(State) State {
		return func(s State) State {
			s.Y = y
			return s
		}
	}, fetchString).
	Let(func(x int) func(State) State {
		return func(s State) State {
			s.X = x
			return s
		}
	}, func(s State) int {
		return len(s.Y)
	})

Bind Operations

The package provides various bind operations for integrating with other effect types:

  • BindIOK: Bind an IO operation
  • BindIOEitherK: Bind an IOEither operation
  • BindIOResultK: Bind an IOResult operation
  • BindReaderK: Bind a Reader operation
  • BindReaderIOK: Bind a ReaderIO operation
  • BindEitherK: Bind an Either operation

Each bind operation has a corresponding "L" variant for working with lenses:

  • BindL, BindIOKL, BindReaderKL, etc.

Applicative Operations

Apply effects in parallel:

// Apply a function effect to a value effect
effect.Ap[string, MyContext](valueEffect)(functionEffect)

// Apply effects to build up a structure
effect.ApS[MyContext](setter, effect1)

Traversal

Traverse collections with effects:

// Map an array with an effectful function
effect.TraverseArray[MyContext](func(x int) Effect[MyContext, string] {
	return effect.Succeed[MyContext, string](strconv.Itoa(x))
})

Retry Logic

Retry effects with configurable policies:

effect.Retrying[MyContext, string](
	retryPolicy,
	func(status retry.RetryStatus) Effect[MyContext, string] {
		return fetchData()
	},
	func(result Result[string]) bool {
		return result.IsLeft() // retry on error
	},
)

Monoids

Combine effects using monoid operations:

// Combine effects using applicative semantics
effect.ApplicativeMonoid[MyContext](stringMonoid)

// Combine effects using alternative semantics (first success)
effect.AlternativeMonoid[MyContext](stringMonoid)

Running Effects

To execute an effect:

// Provide the context
ioResult := effect.Provide[MyContext, string](myContext)(myEffect)

// Run synchronously
readerResult := effect.RunSync(ioResult)

// Execute with a context.Context
value, err := readerResult(ctx)

Integration with Other Packages

The effect package integrates seamlessly with other fp-go packages:

  • either: For error handling
  • io: For I/O operations
  • reader: For dependency injection
  • result: For result types
  • retry: For retry logic
  • monoid: For combining effects

Example

type Config struct {
	APIKey string
	BaseURL string
}

func fetchUser(id int) Effect[Config, User] {
	return effect.Chain[Config](func(cfg Config) Effect[Config, User] {
		// Use cfg.APIKey and cfg.BaseURL
		return effect.Succeed[Config, User](User{ID: id})
	})(effect.Of[Config, Config](Config{}))
}

func main() {
	cfg := Config{APIKey: "key", BaseURL: "https://api.example.com"}
	userEffect := fetchUser(42)

	// Run the effect
	ioResult := effect.Provide(cfg)(userEffect)
	readerResult := effect.RunSync(ioResult)
	user, err := readerResult(context.Background())

	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("User: %+v\n", user)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LocalEffectK

func LocalEffectK[A, C1, C2 any](f Kleisli[C2, C2, C1]) func(Effect[C1, A]) Effect[C2, A]

LocalEffectK transforms the context of an Effect using an Effect-returning function. This is the most powerful context transformation function, allowing the transformation itself to be effectful (can fail, perform I/O, and access the outer context).

LocalEffectK takes a Kleisli arrow that:

  • Accepts the outer context C2
  • Returns an Effect that produces the inner context C1
  • Can fail with an error during context transformation
  • Can perform I/O operations during transformation

This is useful when:

  • Context transformation requires I/O (e.g., loading config from a file)
  • Context transformation can fail (e.g., validating or parsing context)
  • Context transformation needs to access the outer context

Type Parameters:

  • A: The value type produced by the effect
  • C1: The inner context type (required by the original effect)
  • C2: The outer context type (provided to the transformed effect)

Parameters:

  • f: A Kleisli arrow (C2 -> Effect[C2, C1]) that transforms C2 to C1 effectfully

Returns:

  • A function that transforms Effect[C1, A] to Effect[C2, A]

Example:

type DatabaseConfig struct {
	ConnectionString string
}

type AppConfig struct {
	ConfigPath string
}

// Effect that needs DatabaseConfig
dbEffect := effect.Of[DatabaseConfig, string]("query result")

// Transform AppConfig to DatabaseConfig effectfully
// (e.g., load config from file, which can fail)
loadConfig := func(app AppConfig) Effect[AppConfig, DatabaseConfig] {
	return effect.Chain[AppConfig](func(_ AppConfig) Effect[AppConfig, DatabaseConfig] {
		// Simulate loading config from file (can fail)
		return effect.Of[AppConfig, DatabaseConfig](DatabaseConfig{
			ConnectionString: "loaded from " + app.ConfigPath,
		})
	})(effect.Of[AppConfig, AppConfig](app))
}

// Apply the transformation
transform := effect.LocalEffectK[string, DatabaseConfig, AppConfig](loadConfig)
appEffect := transform(dbEffect)

// Run with AppConfig
ioResult := effect.Provide(AppConfig{ConfigPath: "/etc/app.conf"})(appEffect)
readerResult := effect.RunSync(ioResult)
result, err := readerResult(context.Background())

Comparison with other Local functions:

  • Local/Contramap: Pure context transformation (C2 -> C1)
  • LocalIOK: IO-based transformation (C2 -> IO[C1])
  • LocalIOResultK: IO with error handling (C2 -> IOResult[C1])
  • LocalReaderIOResultK: Reader-based with IO and errors (C2 -> ReaderIOResult[C1])
  • LocalEffectK: Full Effect transformation (C2 -> Effect[C2, C1])

func LocalIOK

func LocalIOK[A, C1, C2 any](f io.Kleisli[C2, C1]) func(Effect[C1, A]) Effect[C2, A]

func LocalIOResultK

func LocalIOResultK[A, C1, C2 any](f ioresult.Kleisli[C2, C1]) func(Effect[C1, A]) Effect[C2, A]

func LocalResultK

func LocalResultK[A, C1, C2 any](f result.Kleisli[C2, C1]) func(Effect[C1, A]) Effect[C2, A]

func LocalThunkK

func LocalThunkK[A, C1, C2 any](f thunk.Kleisli[C2, C1]) func(Effect[C1, A]) Effect[C2, A]

func Provide

func Provide[C, A any](c C) func(Effect[C, A]) ReaderIOResult[A]

func Read

func Read[A, C any](c C) func(Effect[C, A]) Thunk[A]

func RunSync

func RunSync[A any](fa ReaderIOResult[A]) readerresult.ReaderResult[A]

Types

type Effect

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

func Do

func Do[C, S any](
	empty S,
) Effect[C, S]

func Fail

func Fail[C, A any](err error) Effect[C, A]

func Of

func Of[C, A any](a A) Effect[C, A]

func Retrying

func Retrying[C, A any](
	policy retry.RetryPolicy,
	action Kleisli[C, retry.RetryStatus, A],
	check Predicate[Result[A]],
) Effect[C, A]

func Succeed

func Succeed[C, A any](a A) Effect[C, A]

func Suspend

func Suspend[C, A any](fa Lazy[Effect[C, A]]) Effect[C, A]

type Either

type Either[E, A any] = either.Either[E, A]

type IO

type IO[A any] = io.IO[A]

type IOEither

type IOEither[E, A any] = ioeither.IOEither[E, A]

type IOResult

type IOResult[A any] = ioresult.IOResult[A]

type Kleisli

type Kleisli[C, A, B any] = readerreaderioresult.Kleisli[C, A, B]

func Contramap

func Contramap[C1, C2, A any](acc Reader[C1, C2]) Kleisli[C1, Effect[C2, A], A]

func Local

func Local[C1, C2, A any](acc Reader[C1, C2]) Kleisli[C1, Effect[C2, A], A]

func Ternary

func Ternary[C, A, B any](pred Predicate[A], onTrue, onFalse Kleisli[C, A, B]) Kleisli[C, A, B]

func TraverseArray

func TraverseArray[C, A, B any](f Kleisli[C, A, B]) Kleisli[C, []A, []B]

type Lazy

type Lazy[A any] = lazy.Lazy[A]

type Lens

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

type Monoid

type Monoid[A any] = monoid.Monoid[A]

func AlternativeMonoid

func AlternativeMonoid[C, A any](m monoid.Monoid[A]) Monoid[Effect[C, A]]

func ApplicativeMonoid

func ApplicativeMonoid[C, A any](m monoid.Monoid[A]) Monoid[Effect[C, A]]

type Operator

type Operator[C, A, B any] = readerreaderioresult.Operator[C, A, B]

func Ap

func Ap[B, C, A any](fa Effect[C, A]) Operator[C, func(A) B, B]

func ApEitherS

func ApEitherS[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Either[error, T],
) Operator[C, S1, S2]

func ApEitherSL

func ApEitherSL[C, S, T any](
	lens Lens[S, T],
	fa Either[error, T],
) Operator[C, S, S]

func ApIOEitherS

func ApIOEitherS[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa IOEither[error, T],
) Operator[C, S1, S2]

func ApIOEitherSL

func ApIOEitherSL[C, S, T any](
	lens Lens[S, T],
	fa IOEither[error, T],
) Operator[C, S, S]

func ApIOS

func ApIOS[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa IO[T],
) Operator[C, S1, S2]

func ApIOSL

func ApIOSL[C, S, T any](
	lens Lens[S, T],
	fa IO[T],
) Operator[C, S, S]

func ApReaderIOS

func ApReaderIOS[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa ReaderIO[C, T],
) Operator[C, S1, S2]

func ApReaderIOSL

func ApReaderIOSL[C, S, T any](
	lens Lens[S, T],
	fa ReaderIO[C, T],
) Operator[C, S, S]

func ApReaderS

func ApReaderS[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Reader[C, T],
) Operator[C, S1, S2]

func ApReaderSL

func ApReaderSL[C, S, T any](
	lens Lens[S, T],
	fa Reader[C, T],
) Operator[C, S, S]

func ApS

func ApS[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Effect[C, T],
) Operator[C, S1, S2]

func ApSL

func ApSL[C, S, T any](
	lens Lens[S, T],
	fa Effect[C, T],
) Operator[C, S, S]

func Bind

func Bind[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f Kleisli[C, S1, T],
) Operator[C, S1, S2]

func BindEitherK

func BindEitherK[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f either.Kleisli[error, S1, T],
) Operator[C, S1, S2]

func BindIOEitherK

func BindIOEitherK[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f ioeither.Kleisli[error, S1, T],
) Operator[C, S1, S2]

func BindIOEitherKL

func BindIOEitherKL[C, S, T any](
	lens Lens[S, T],
	f ioeither.Kleisli[error, T, T],
) Operator[C, S, S]

func BindIOK

func BindIOK[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f io.Kleisli[S1, T],
) Operator[C, S1, S2]

func BindIOKL

func BindIOKL[C, S, T any](
	lens Lens[S, T],
	f io.Kleisli[T, T],
) Operator[C, S, S]

func BindIOResultK

func BindIOResultK[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f ioresult.Kleisli[S1, T],
) Operator[C, S1, S2]

func BindL

func BindL[C, S, T any](
	lens Lens[S, T],
	f func(T) Effect[C, T],
) Operator[C, S, S]

func BindReaderIOK

func BindReaderIOK[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f readerio.Kleisli[C, S1, T],
) Operator[C, S1, S2]

func BindReaderIOKL

func BindReaderIOKL[C, S, T any](
	lens Lens[S, T],
	f readerio.Kleisli[C, T, T],
) Operator[C, S, S]

func BindReaderK

func BindReaderK[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f reader.Kleisli[C, S1, T],
) Operator[C, S1, S2]

func BindReaderKL

func BindReaderKL[C, S, T any](
	lens Lens[S, T],
	f reader.Kleisli[C, T, T],
) Operator[C, S, S]

func BindTo

func BindTo[C, S1, T any](
	setter func(T) S1,
) Operator[C, T, S1]

func Chain

func Chain[C, A, B any](f Kleisli[C, A, B]) Operator[C, A, B]

func ChainResultK

func ChainResultK[C, A, B any](f result.Kleisli[A, B]) Operator[C, A, B]

func Let

func Let[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	f func(S1) T,
) Operator[C, S1, S2]

func LetL

func LetL[C, S, T any](
	lens Lens[S, T],
	f func(T) T,
) Operator[C, S, S]

func LetTo

func LetTo[C, S1, S2, T any](
	setter func(T) func(S1) S2,
	b T,
) Operator[C, S1, S2]

func LetToL

func LetToL[C, S, T any](
	lens Lens[S, T],
	b T,
) Operator[C, S, S]

func Map

func Map[C, A, B any](f func(A) B) Operator[C, A, B]

func Tap

func Tap[C, A, ANY any](f Kleisli[C, A, ANY]) Operator[C, A, A]

type Predicate

type Predicate[A any] = predicate.Predicate[A]

type Reader

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

type ReaderIO

type ReaderIO[R, A any] = readerio.ReaderIO[R, A]

type ReaderIOResult

type ReaderIOResult[A any] = readerioresult.ReaderIOResult[A]

type Result

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

type Thunk

type Thunk[A any] = ReaderIOResult[A]

Jump to

Keyboard shortcuts

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