I'm writing a wrapper around a Warp server where users can specify routes and handlers to make a web server. I decided to try using Continuation Monads to allow handlers to exit using a continuation when a route matches. Here are the types and definitions I'm starting with:
import Control.Monad.Cont
import Control.Monad.Reader
import qualified Network.Wai as W
import qualified Data.Text as T
type App r a = ContT r (ReaderT W.Request IO) a
type Handler a = ReaderT W.Request IO a
type Respond = ((Status, T.Text) -> App (Status, T.Text) ())
route :: T.Text -> Handler (Status, T.Text) -> Respond -> App (Status, T.Text) ()
route routePath handler respond = do
liftIO $ print $ "Checking" `T.append` routePath
pth <- path
when (routePath == pth) $ do
req <- ask
response <- liftIO $ runReaderT handler req
respond response
An app is a collection of routes, each route reads the current continuation from the Reader environment; I originally wrote it like this:
hello :: Handler (Status, T.Text)
hello = return (ok200, "Hello World!")
goodbye :: Handler (Status, T.Text)
goodbye = return (ok200, "Goodbye World!")
app :: Respond -> App (Status, T.Text) ()
app = do
route "/hello" hello
route "/goodbye" goodbye
Strangely this doesn't seem to work, it only prints "Checking /goodbye"; however if we instead write the reader in the next form it works properly, as far as I was aware these two definitions should be equivalent; but apparently I'm missing something:
app :: Respond -> App (Status, T.Text) ()
app resp = do
route "/hello" hello resp
route "/goodbye" goodbye resp
Is there any way I can get the proper behaviour using the original app definition? Is there some way that the Reader Monad is messing up the continuations somehow?
I suspect that somehow the monad definition for reader is interrupting the order of computation; but it's not clear to me how:
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
Thanks!