danidiaz / dep-t

Dependency injection for records-of-functions.
http://hackage.haskell.org/package/dep-t
BSD 3-Clause "New" or "Revised" License
8 stars 2 forks source link
dependency-injection haskell monad

dep-t

This package provides various helpers for the "record-of-functions" style of structuring Haskell applications.

A record that groups related functions is considered a component. Hypothetical example:

data Repository m = Repository
  { findById :: ResourceId -> m Resource,
    save :: Resource -> m ()
  } 

The record type is the component's "interface". A component's "implementation" is defined by a constructor function that returns a value of the record type.

When starting up, applications build a dependency injection environment which contains all the required components. And components read their own dependencies from the DI environment. The DI environment is akin to an ApplicationContext in object-oriented frameworks like Java Spring.

If components knew about the concrete DI environment, that would increase coupling. Everything would depend on everything else. To avoid that, we resort to Has-style typeclasses so that each constructor function knows only about the parts of the environment that it needs, and nothing more. Those Has-style classes can be tailor-made, but this package also provides a generic one.

Hypothetical example of constructor function:

makeRepository :: (Has Logger m deps, Has SomeOtherDep m deps) => deps -> Repository m

Very loosely speaking, Has-style constraints correspond to injected constructor arguments in object-oriented DI frameworks.

Module structure

  graph TD;
      Dep.Env-->Dep.Has;
      Dep.Env-->Dep.Phases;
      Dep.Constructor-->Dep.Phases;
      Control.Monad.Dep.Class-->Control.Monad.Reader;
      Control.Monad.Dep-->Control.Monad.Reader;
      Control.Monad.Dep-->Control.Monad.Dep.Class;

Links