Skip to content

Instantly share code, notes, and snippets.

@thelissimus
Created May 12, 2024 09:25
Show Gist options
  • Save thelissimus/6c4722b52ca44ecc266e99833ab3119f to your computer and use it in GitHub Desktop.
Save thelissimus/6c4722b52ca44ecc266e99833ab3119f to your computer and use it in GitHub Desktop.
Dependency Injection in Haskell.
{-# LANGUAGE ImplicitParams #-}
module DI (module DI) where
import Control.Monad.Reader (MonadReader (ask), ReaderT (runReaderT))
data Database = MkDatabase
data User = MkUser deriving (Show)
data Request = MkRequest Int
data Response = MkResponse String
-- 1: Passing params manually
getUser :: Database -> Int -> IO User
getUser db _id = do
-- use db here
pure MkUser
handleGet :: Database -> Request -> IO Response
handleGet db (MkRequest _id) = do
user <- getUser db _id -- see the problem? have to manually wire everything
pure (MkResponse (show user))
-- 2: Composing params with ReaderT
getUserR :: Int -> ReaderT Database IO User
getUserR _id = do
db <- ask -- use db here
pure MkUser
handleGetR :: Request -> ReaderT Database IO Response
handleGetR (MkRequest _id) = do
user <- getUserR _id -- no need to pass the db manually — it is passed automatically
pure (MkResponse (show user))
-- 3: Passing params implicitly
getUserI :: (?db :: Database) => Int -> IO User
getUserI _id = do
-- use ?db here
pure MkUser
handleGetI :: (?db :: Database) => Request -> IO Response
handleGetI (MkRequest _id) = do
user <- getUserI _id -- still no need — it is passed implicitly
pure (MkResponse (show user))
main :: IO ()
main = do
let db = MkDatabase
-- Passing params manually
res <- handleGet db (MkRequest 42)
-- Passing params through Reader composition
resR <- runReaderT (handleGetR (MkRequest 42)) db
-- Creating an implicit param and passing it implicitly
let ?db = MkDatabase
resI <- handleGetI (MkRequest 42)
pure ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment