Skip to content

example of an approach to modular development in haskell

License

Notifications You must be signed in to change notification settings

toddaaro/haskell-di

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dependency Injection in Haskell

This repo is an example of doing SMD (separate modular development) in Haskell. Currently doing so is challenging, as the Haskell module system was not designed to support this. Quotes from the Backpack paper:

Suppose an author A wants to write, test, and publish a software component (or “package”) P, that needs to call a random-number generator. But A wants each customer C to be able to supply his or her own random-number generator. In a typed language, the author A must define the interface to the random-number generator, typecheck P with respect to the interface, and publish P. The client C then links P with a particular random-number generator that matches the interface. We refer to this style of development as separate modular development (SMD), as distinct from the style of incremental modular development (IMD) in which a package can only be typechecked when the implementations of its dependencies are available.

Haskell’s existing module system was consciously designed as a weak namespace management system without a proper notion of interface, and hence supports only IMD, not SMD. Tools like the Cabal package management system pick up the slack, enabling users to (ab)use version-range dependencies in order to work around the lack of interfaces. But the Haskell community recognizes that this is a makeshift solution and is actively seeking ways to support SMD properly.

While the new Backpack system looks to solve this problem, it is still far away as a well-supported easy-to-use part of the ecosystem, so the following is an approach we can use immediately.

Basic Approach

The core of this approach is to use typeclasses to specify module interfaces, then create module instances as ADTs that implement those typeclasses. The ADT acts as the module constructor, taking in its dependency modules as arguments to its data constructor. Then the instance of the interface typeclass for that specific ADT can consume those dependencies to provide implementations. The approach is complicated by monadic code, but with a few type system pragmas it seems to work fine.

Unknowns

I am not yet a proficient Haskell programmer, so it is possible this is doable in a much simpler way. Of particular difficulty was getting the type inference to work and correctly assemble things; playing whack a mole with pragmas got me there but hopefully not all of them are necessary.

It is also unclear how resilient this approach is to cargo culting programmers, or even well-intentioned programmers who are under duress and don't have time to think carefully about the whole dependency tree.

About

example of an approach to modular development in haskell

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published