我正在围绕Warp服务器编写一个包装器,用户可以在其中指定路由和处理程序来创建Web服务器。我决定尝试使用Continuation Monads来允许处理程序在路由匹配时使用continuation退出。 以下是我开始使用的类型和定义:
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
app是路由的集合,每个路由从Reader环境读取当前的continuation;我原来是这样写的:
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
奇怪的是,这似乎不起作用,它只打印&#34;检查/再见&#34 ;;但是,如果我们改为以下一种形式编写阅读器,它就能正常工作,据我所知,这两个定义应该是等价的;但显然我错过了一些东西:
app :: Respond -> App (Status, T.Text) ()
app resp = do
route "/hello" hello resp
route "/goodbye" goodbye resp
有什么方法可以使用原始应用定义获得正确的行为?读者Monad是否有某种方式以某种方式搞乱延续?
我怀疑读者的monad定义在某种程度上打断了计算的顺序;但我不清楚如何:
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
谢谢!