Skip to content

Instantly share code, notes, and snippets.

@tcrayford
Created December 18, 2012 17:04
Show Gist options
  • Save tcrayford/4329803 to your computer and use it in GitHub Desktop.
Save tcrayford/4329803 to your computer and use it in GitHub Desktop.
class Responder a where
respond :: a -> Controller ()
class Injectable a where
inject :: Controller (Maybe a)
instance (Injectable a, Responder b) => Responder (a -> b) where
respond f = do
a <- inject
case a of
(Just arg) -> respond $ f arg
Nothing -> pass
-- then when wiring your handlers up into routes, you call `respond` on each handler
-- and each handler can take any arguments that are "injected" out of the web request
-- an example
newtype UserId = UserId Int deriving (Show, Eq)
newtype Email = Email Text deriving (Show, Eq)
data CurrentUser = CurrentUser UserId Email deriving (Show, Eq)
instance Injectable CurrentUser where
inject = do
userId <- getIdFromSession
record $ findByUserId userId
editPost :: CurrentUser -> Controller ()
editPost u = undefined
-- record runs a custom monad, which is basically a `ReaderT` of the db connection, which is also contained inside my
-- `Controller` monad
-- `Controller` is just a ReaderT wrapper around the snap monad that lets me hide the fact that controller
-- actions need to pass the database connection to the db layer
-- This isn't perfect, because we can't respond in a custom way when the injection fails.
-- Ideally inject would have the following type signature:
-- class Injectable a where
-- inject :: (Responder b) => Controller (Either b a)
-- but I couldn't make the compiler happy with that yet. Might need multiparam typeclasses or summat :(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment