1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00

fix: experiment with doc links

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-12-19 12:18:07 +01:00
parent 2b500d15da
commit a6f55a199c
8 changed files with 56 additions and 41 deletions

View File

@@ -62,7 +62,7 @@ func generateMakeProvider(f *os.File, i int) {
func generateMakeTokenWithDefault(f *os.File, i int) { func generateMakeTokenWithDefault(f *os.File, i int) {
// non generic version // non generic version
fmt.Fprintf(f, "\n// MakeTokenWithDefault%d creates an [InjectionToken] with a default implementation with %d dependenciess\n", i, i) fmt.Fprintf(f, "\n// MakeTokenWithDefault%d creates an [InjectionToken] with a default implementation with %d dependencies\n", i, i)
fmt.Fprintf(f, "func MakeTokenWithDefault%d[", i) fmt.Fprintf(f, "func MakeTokenWithDefault%d[", i)
for j := 0; j < i; j++ { for j := 0; j < i; j++ {
if j > 0 { if j > 0 {

View File

@@ -14,6 +14,21 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package implements functions and data types supporting dependency injection patterns
//
// The fundamental building block is the concept of a [Dependency]. This describes the abstract concept of a function, service or value together with its type.
// Examples for dependencies can be as simple as configuration values such as the API URL for a service, the current username, a [Dependency] could be the map
// of the configuration environment, an http client or as complex as a service interface. Important is that a [Dependency] only defines the concept but
// not the implementation.
//
// The implementation of a [Dependency] is called a [Provider], the dependency of an `API URL` could e.g. be realized by a provider that consults the environment to read the information
// or a config file or simply hardcode it.
// In many cases the implementation of a [DIE.Provider] depends in turn on other [Dependency]s (but never directly on other [DIE.Provider]s), a provider for an `API URL` that reads
// the information from the environment would e.g. depend on a [Dependency] that represents this environment.
//
// It is the resposibility of the [DIE.InjectableFactory] to
//
// [Provider]: [github.com/IBM/fp-go/di/erasure.Provider]
package di package di
//go:generate go run .. di --count 10 --filename gen.go //go:generate go run .. di --count 10 --filename gen.go

View File

@@ -115,6 +115,9 @@ func itemProviderFactory(fcts []ProviderFactory) ProviderFactory {
} }
// MakeInjector creates an [InjectableFactory] based on a set of [Provider]s // MakeInjector creates an [InjectableFactory] based on a set of [Provider]s
//
// The resulting [InjectableFactory] can then be used to retrieve service instances given their [Dependency]. The implementation
// makes sure to transitively resolve the required dependencies.
func MakeInjector(providers []Provider) InjectableFactory { func MakeInjector(providers []Provider) InjectableFactory {
type Result = IOE.IOEither[error, any] type Result = IOE.IOEither[error, any]

View File

@@ -32,6 +32,7 @@ import (
) )
type ( type (
// InjectableFactory is a factory function that can create an untyped instance of a service based on its [Dependency] identifier
InjectableFactory = func(Dependency) IOE.IOEither[error, any] InjectableFactory = func(Dependency) IOE.IOEither[error, any]
ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any] ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any]
@@ -83,6 +84,8 @@ var (
mergeMaps = R.UnionLastMonoid[int, any]() mergeMaps = R.UnionLastMonoid[int, any]()
collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any]) collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any])
mapDeps = F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])
handlers = map[int]handler{ handlers = map[int]handler{
Identity: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { Identity: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
@@ -171,7 +174,7 @@ func MakeProviderFactory(
fct func(param ...any) IOE.IOEither[error, any]) ProviderFactory { fct func(param ...any) IOE.IOEither[error, any]) ProviderFactory {
return F.Flow3( return F.Flow3(
F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])(deps), mapDeps(deps),
handleMapping(foldDeps(deps)), handleMapping(foldDeps(deps)),
IOE.Chain(F.Unvariadic0(fct)), IOE.Chain(F.Unvariadic0(fct)),
) )

View File

@@ -39,17 +39,17 @@ func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error,
) )
} }
func eraseProviderFactory0[R any](f func() IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
return func(params ...any) IOE.IOEither[error, any] { return func(params ...any) IOE.IOEither[error, any] {
return F.Pipe1( return F.Pipe1(
f(), f,
IOE.Map[error](F.ToAny[R]), IOE.Map[error](F.ToAny[R]),
) )
} }
} }
func MakeProviderFactory0[R any]( func MakeProviderFactory0[R any](
fct func() IOE.IOEither[error, R], fct IOE.IOEither[error, R],
) DIE.ProviderFactory { ) DIE.ProviderFactory {
return DIE.MakeProviderFactory( return DIE.MakeProviderFactory(
A.Empty[DIE.Dependency](), A.Empty[DIE.Dependency](),
@@ -57,14 +57,14 @@ func MakeProviderFactory0[R any](
) )
} }
// MakeTokenWithDefault0 create a unique `InjectionToken` for a specific type with an attached default provider // MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider]
func MakeTokenWithDefault0[R any](name string, fct func() IOE.IOEither[error, R]) InjectionToken[R] { func MakeTokenWithDefault0[R any](name string, fct IOE.IOEither[error, R]) InjectionToken[R] {
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct)) return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
} }
func MakeProvider0[R any]( func MakeProvider0[R any](
token InjectionToken[R], token InjectionToken[R],
fct func() IOE.IOEither[error, R], fct IOE.IOEither[error, R],
) DIE.Provider { ) DIE.Provider {
return DIE.MakeProvider( return DIE.MakeProvider(
token, token,
@@ -74,5 +74,5 @@ func MakeProvider0[R any](
// ConstProvider simple implementation for a provider with a constant value // ConstProvider simple implementation for a provider with a constant value
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider { func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
return MakeProvider0[R](token, F.Constant(IOE.Of[error](value))) return MakeProvider0[R](token, IOE.Of[error](value))
} }

View File

@@ -38,12 +38,10 @@ func TestSimpleProvider(t *testing.T) {
var staticCount int var staticCount int
staticValue := func(value string) func() IOE.IOEither[error, string] { staticValue := func(value string) IOE.IOEither[error, string] {
return func() IOE.IOEither[error, string] { return func() E.Either[error, string] {
return func() E.Either[error, string] { staticCount++
staticCount++ return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
}
} }
} }
@@ -82,12 +80,10 @@ func TestOptionalProvider(t *testing.T) {
var staticCount int var staticCount int
staticValue := func(value string) func() IOE.IOEither[error, string] { staticValue := func(value string) IOE.IOEither[error, string] {
return func() IOE.IOEither[error, string] { return func() E.Either[error, string] {
return func() E.Either[error, string] { staticCount++
staticCount++ return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
}
} }
} }
@@ -182,12 +178,10 @@ func TestEagerAndLazyProvider(t *testing.T) {
var staticCount int var staticCount int
staticValue := func(value string) func() IOE.IOEither[error, string] { staticValue := func(value string) IOE.IOEither[error, string] {
return func() IOE.IOEither[error, string] { return func() E.Either[error, string] {
return func() E.Either[error, string] { staticCount++
staticCount++ return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
}
} }
} }
@@ -307,7 +301,7 @@ func TestTokenWithDefaultProvider(t *testing.T) {
// token without a default // token without a default
injToken1 := MakeToken[string]("Token1") injToken1 := MakeToken[string]("Token1")
// token with a default // token with a default
injToken2 := MakeTokenWithDefault0("Token2", F.Constant(IOE.Of[error]("Carsten"))) injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
// dependency // dependency
injToken3 := MakeToken[string]("Token3") injToken3 := MakeToken[string]("Token3")
@@ -330,7 +324,7 @@ func TestTokenWithDefaultProvider(t *testing.T) {
func TestTokenWithDefaultProviderAndOverride(t *testing.T) { func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
// token with a default // token with a default
injToken2 := MakeTokenWithDefault0("Token2", F.Constant(IOE.Of[error]("Carsten"))) injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
// dependency // dependency
injToken3 := MakeToken[string]("Token3") injToken3 := MakeToken[string]("Token3")

View File

@@ -42,20 +42,21 @@ type InjectionToken[T any] interface {
// Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`. // Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`.
// If the dependency cannot be resolved, the resolution process fails // If the dependency cannot be resolved, the resolution process fails
Identity() Dependency[T] Identity() Dependency[T]
// Option identifies this dependency as optional, it will be resolved eagerly and injected as `O.Option[T]`. // Option identifies this dependency as optional, it will be resolved eagerly and injected as [O.Option[T]].
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as `O.None[T]` // If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]]
Option() Dependency[O.Option[T]] Option() Dependency[O.Option[T]]
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a `IOE.IOEither[error, T]`. This // IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOE.IOEither[error, T]]. This
// value is memoized to make sure the dependency is a singleton. // value is memoized to make sure the dependency is a singleton.
// If the dependency cannot be resolved, the resolution process fails // If the dependency cannot be resolved, the resolution process fails
IOEither() Dependency[IOE.IOEither[error, T]] IOEither() Dependency[IOE.IOEither[error, T]]
// IOOption identifies this dependency as optional but it will be resolved lazily as a `IOO.IOOption[T]`. This // IOOption identifies this dependency as optional but it will be resolved lazily as a [IOO.IOOption[T]]. This
// value is memoized to make sure the dependency is a singleton. // value is memoized to make sure the dependency is a singleton.
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value. // If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value.
IOOption() Dependency[IOO.IOOption[T]] IOOption() Dependency[IOO.IOOption[T]]
} }
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name. // MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations.
// Implementations are provided via the [MultiInjectionToken.Item] injection token.
type MultiInjectionToken[T any] interface { type MultiInjectionToken[T any] interface {
// Container returns the injection token used to request an array of all provided items // Container returns the injection token used to request an array of all provided items
Container() InjectionToken[[]T] Container() InjectionToken[[]T]
@@ -146,7 +147,7 @@ func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
return m.item return m.item
} }
// makeToken create a unique `InjectionToken` for a specific type // makeToken create a unique [InjectionToken] for a specific type
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] { func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
id := genId() id := genId()
toIdentity := toType[T]() toIdentity := toType[T]()
@@ -158,12 +159,12 @@ func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.Provide
} }
} }
// MakeToken create a unique `InjectionToken` for a specific type // MakeToken create a unique [InjectionToken] for a specific type
func MakeToken[T any](name string) InjectionToken[T] { func MakeToken[T any](name string) InjectionToken[T] {
return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]()) return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]())
} }
// MakeToken create a unique `InjectionToken` for a specific type // MakeToken create a unique [InjectionToken] for a specific type
func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactory) InjectionToken[T] { func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactory) InjectionToken[T] {
return makeInjectionToken[T](name, O.Of(providerFactory)) return makeInjectionToken[T](name, O.Of(providerFactory))
} }

View File

@@ -21,13 +21,12 @@ import (
DI "github.com/IBM/fp-go/di" DI "github.com/IBM/fp-go/di"
IOE "github.com/IBM/fp-go/ioeither" IOE "github.com/IBM/fp-go/ioeither"
IOEH "github.com/IBM/fp-go/ioeither/http" IOEH "github.com/IBM/fp-go/ioeither/http"
L "github.com/IBM/fp-go/lazy"
) )
var ( var (
// InjHttpClient is the injection token for the default http client // InjHttpClient is the [DI.InjectionToken] for the [http.DefaultClient]
InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", L.Of(IOE.Of[error](http.DefaultClient))) InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", IOE.Of[error](http.DefaultClient))
// InjClient is the injection token for the default [Client] // InjClient is the [DI.InjectionToken] for the default [IOEH.Client]
InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](IOEH.MakeClient)) InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](IOEH.MakeClient))
) )