liveBook · Manning

Dependency injection

As explained in Functional Design and Architecture: Examples in Haskell

Dependency injection is a technique for supplying a component's external functionality through an abstraction rather than constructing or selecting its concrete implementation internally.

Dependency injection supplies business logic with external capabilities through an interface. The logic uses the abstraction while another part of the application chooses and passes a concrete implementation, keeping implementation details outside the business layer. section 4.2.2 section 13.2.1

The problem is high coupling: business logic that reaches directly into external subsystems becomes fragile, difficult to extend, and difficult to test. An interface creates a deliberate extensibility point and lets the application configuration select a reporting channel without exposing HTTP, TCP, or other transport details to reporting logic. section 13.2 section 13.2.1

Injecting a service handle

A GreetingsHandle groups greeting operations, including a pure operation and one explicitly allowed to perform IO. helloUser accepts the handle as an argument and calls its selected operation; main can run the same business flow with either handle1 or handle2. The client code works against the handle rather than knowing which greeting implementation it received. section 4.2.2

In functional designs, dependency injection implements the Dependency Inversion principle: business logic depends on an interface, not concrete implementation details. Interfaces can be API-like, scenario-like, or combinatorial, with several ways to represent and inject them. section 13.3 section 13.2

Common functional approaches trade simplicity, boilerplate, convenience, and language-feature requirements differently:

  • Service Handle passes an interface value directly; it is a very simple approach, though many handles can become unpleasant to manage. section 4.2.2 section 13.2.7
  • ReaderT extends the Service Handle idea by storing the handle in a Reader environment, with more boilerplate from lifting. section 13.3 section 13.2.7
  • Free monads, GADTs, and final tagless provide other interface styles; final tagless relies on features such as type classes and phantom types. section 13.3 section 13.2.7

Injection also supports testing impure code. A test can redirect a database call to a mocked service that fails at a chosen point, allowing business logic to be checked against controlled external behavior that is hard to reproduce with integration testing alone. section 15.1 section 15.2.1

Listing 4.8 Injecting the dependency with service handle
helloUser :: GreetingsHandle -> IO ()   #1
helloUser (GreetingsHandle _ sayHi) = do
  putStrLn "What is your name?"
  name <- getLine
  _ <- sayHi name                       #2
  pure ()
  
main :: IO ()
main = do
  helloUser handle1                     #3
  helloUser handle2                     #4

Compare a function that receives a GreetingsHandle with the application entry point that chooses two concrete handle values. section 4.2.2

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains direct handle injection, compares several functional interface styles, and notes their practical trade-offs.
Examples
Demonstrates greeting handles, configurable meteor and asteroid reporting, and mocked database failures in tests.
What it assumes
Assumes functional-programming context while introducing Service Handle as a simple interface before more advanced alternatives.
What you can do after
Apply it via Service Handle or other functional interfaces to reduce coupling and control test environments.
How it connects
Connects dependency injection to Dependency Inversion, Service Handle, ReaderT, Free monads, GADTs, final tagless, and mocking.

Also explained in

Compare how 21 books explain dependency injection →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Design and Architecture: Examples in Haskell. The section links point to the parts of the book it draws on.