indi

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Dec 2, 2023 License: MIT Imports: 4 Imported by: 0

README

indi: simple initialization optimizer

Parallelize initialization of your dependencies with ease.

The problem

Imagine you have a big application that access same stateful resources in different places. You write something like this (go style!):

func main() {
    a := a.New()
    b := b.New(a)
    c := c.New(a, b)
}

Every step requires some time. You want to spend as little time as possible.

Now, it's not hard to see that the way initialization process goes is kind of the best already. You can't start with initializing c because it depends on a and b. And b depends on a. And you can't parallelize anything.

But as your code base grows, you have more and more such dependencies. And it becomes harder to keep track of them and initialize them the optimal way. You try to initialize several things in parallel.

func main() {
	var (
        a *A
        b *B
        ...
        z *Z
    )

    aReady := make(chan struct{})
    var wg sync.WaitGroup
    wg.Add(3)
	
    go func() {
        defer wg.Done()
        a = a.New()
        close(aReady)
        b = b.New(a)
        c = c.New(a, b)
    }()

    go func() {
        defer wg.Done()
        <-aReady
        d = d.New(a)
        e = e.New(d)
    }()
    
    go func() {
        ...
    }
}

You're not sure anymore that you are doing it the optimal way.

So you need a sort of dependency graph...

Here comes the indi

// Dependency tree:
// A -> B -> C
// A -> D

func main() {
    var (
        a A
        b B
        c C
        d D
    )

    indi.Declare(&a, func () (*A, error) { return NewA(&b, &d) }, &b, &d)
    indi.Declare(&b, func () (*B, error) { return NewB(&c) }, &c)
    indi.Declare(&c, NewC)
    indi.Declare(&d, NewD)

    err := indi.Init()
    ...
}

See example/main.go to get the idea.

You casually declare all the stuff regardless of the order, and indi takes care of it.

P.S. Generics are used instead of interface{} on purpose:

  1. To avoid type assertions (well, there are still some under the hood).
  2. To force you to place all the dirty initialization with actual types in main package, or init package, or wherever but far from the actual code that uses those dependencies. main is usually the place where you pass implementations as interface functions parameters.
  3. To support the principle "accept interfaces, return actual types".
  4. Just for fun!

Enjoy!

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultGraph = make(Graph)
View Source
var (
	ErrInvalidConstructor = errors.New("constructor must return non-nil value if there is no error")
)

Functions

func Declare

func Declare[T any](ptr *T, constructor func() (*T, error), deps ...any)

Declare - declare a node to initialize. Non-blocking operation.

func DeclareOnGraph

func DeclareOnGraph[T any](g Graph, ptr *T, constructor func() (*T, error), deps ...any)

DeclareOnGraph - declare a node to initialize on a graph. Non-blocking operation.

func Init

func Init() error

Init - initialize a default graph. Blocking operation.

func InitGraph

func InitGraph(g Graph) error

InitGraph - initialize a graph. It is a blocking operation.

func LazyLoad added in v0.1.2

func LazyLoad[T, I any](ptr *T) (cb func() (I, error))

LazyLoad - a helper function, wrapping Load. The idea is to initialize something and all it's dependencies without knowing what's the concrete type of it.

func LazyLoadFromGraph added in v0.1.2

func LazyLoadFromGraph[T, I any](g Graph, ptr *T) (cb func() (I, error))

LazyLoadFromGraph - a helper function, wrapping LoadFromGraph. The idea is to initialize something and all it's dependencies without knowing what's the concrete type of it.

func Load added in v0.1.2

func Load[T any](ptr *T) error

Load - initialize a node and all it's dependencies. Blocking operation.

func LoadFromGraph added in v0.1.2

func LoadFromGraph[T any](g Graph, ptr *T) error

LoadFromGraph - initialize a node from a graph and all it's dependencies. Blocking operation.

Types

type Graph

type Graph map[string]*graphNode

Graph - dependency graph. Has no public methods as it shouldn't be used directly. However, it is exported to allow custom dependency graphs to be used with DeclareOnGraph and InitGraph methods:

var g = make(indi.Graph)
indi.DeclareOnGraph(g, ...)
...
indi.InitGraph(g)

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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