Reputation: 16079
This is the first time I'm playing with Monad Transformers. This is a simple happstack app.
{-# LANGUAGE OverloadedStrings #-}
import Happstack.Lite
import qualified Data.ByteString.Lazy.Char8 as L
main :: IO ()
main = do
serve Nothing hello
hello :: ServerPart Response
hello = do
ok $ toResponse ("Hello" :: L.ByteString)
I would like to be able to change hello so it can read some global config data using ReaderT
. Let's just say the config is a string to keep it simple
type NewMonad = ReaderT L.ByteString (ServerPartT IO)
runNewMonad :: NewMonad a -> L.ByteString -> ServerPart a
runNewMonad k c = runReaderT k c
How do I change hello so it can use ask
? I'm not sure what the type would be. NewMonad Response
isn't quite right, because ok
returns a ServerPart Response
.
How do I change main so that serve
works? It expects a ServerPart Response
.
Upvotes: 2
Views: 308
Reputation: 40797
In fact, NewMonad Response
is the correct type for hello
; you just need to use lift
to transform an action in the underlying monad to one in the transformer. For example:
hello :: NewMonad Response
hello = do
foo <- ask
lift . ok $ toResponse foo
In general,
lift :: (MonadTrans t, Monad m) => m a -> t m a
i.e., if you have a monadic action, then you turn it into an action in any monad transformer over that monad. This is the definition of a monad transformer: it can transform over any monad, and embed actions of that monad.
It seems that restricting all the monadic actions to one specific monad — rather than using typeclasses to work in any appropriate monad — is one of the simplifications happstack-lite uses compared to the full Happstack, which has this type for ok
:
ok :: (FilterMonad Response m) => a -> m a
With this type, assuming appropriate instances are declared for the standard transformers, you could just use ok
directly in MyMonad
.
As for main
, you need to eliminate the ReaderT
layer, leading a a ServerPart Response
that you can pass to serve
:
main :: IO ()
main = do
serve Nothing $ runNewMonad hello ("Hello" :: L.ByteString)
(This would cause problems if you were using a monad carrying state that you wanted to change over the course of many requests, since serve
's type is too restrictive to support such state threading (without manually encoding it with IORef
s or similar); possibly the unrestricted Happstack has the ability to do this, but it'd likely be very brittle anyway, as you shouldn't really be relying on the order requests are processed in like that.)
Upvotes: 5