让我们采用haskell中最简单的http服务器
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai (responseLBS, Application)
import Network.Wai.Handler.Warp (run)
import Network.HTTP.Types (status200)
import Network.HTTP.Types.Header (hContentType)
main = do
let port = 3000
putStrLn $ "Listening on port " ++ show port
run port app
app :: Application
app req f =
f $ responseLBS status200 [(hContentType, "text/plain")] "Hello world!"
我该如何在应用程序功能中集成状态单子才能保持网页点击量?我很难用纯函数式语言来思考。也许我缺少一些基础知识
答案 0 :(得分:1)
据我了解,WAI将为每个请求调用一次Application
,并且Application
的类型非常受限制:Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
。 Application
类型的具体性意味着您不能“提升” run
来获取返回StateT s IO ResponseReceived
的函数,这显然是您所想到的方法。如果要在一个请求处理程序中使用State
monad,则可以通过编写代码以使用类似StateT s IO ResponseReceived
的代码,然后使用app req f = runStateT initial $ ...
来实现。但是,这在保留请求处理程序调用之间的状态方面不起作用,就像您想要的那样。不幸的是,我不认为有一种简单的方法可以让您的WAI处理程序在自定义monad中运行,并让WAI服务器保留两次调用之间的monadic状态-从某种意义上讲,这是有道理的,因为期望这样做似乎合理服务器可能希望针对不同的请求并行运行同一处理程序多次。
相反,如果要从处理程序内部跟踪某些状态,最好在IO中使用某种可变状态。例如,一个简单的请求计数器可能是这样的:
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai (responseLBS, Application)
import Network.Wai.Handler.Warp (run)
import Network.HTTP.Types (status200)
import Network.HTTP.Types.Header (hContentType)
import Control.Concurrent.MVar
main = do
let port = 3000
putStrLn $ "Listening on port " ++ show port
hitCounter <- newMVar 0
run port $ app hitCounter
app :: MVar Int -> Application
app hitCounter req f = do
old <- takeMVar hitCounter
let new = old + 1
putMVar hitCounter $! new -- Thanks to Daniel Wagner in the comments for $!
putStrLn $ "Hits: " ++ show new
f $ responseLBS status200 [(hContentType, "text/plain")] "Hello world!"
Haskell中的并发可变状态当然还有许多其他选项,包括IORef
和各种STM
类型。这些中的每一个都有优点/缺点/缺点,在并发/可变状态的一般情况下要意识到这一点很重要,并且许多优点/缺点/缺点应该在此情况下可用。感谢Daniel Wagner在评论中指出,使用putMVar hitCounter new
的该示例的原始版本忽略了一个常见的陷阱:如果不强制输入MVar
的值,则可能会产生重击相反,这通常不是预期的。在上面的代码中,new
由putStrLn
强制执行,但是如果以后将其删除,则优良作法是在将值放入MVar
时立即强制执行此操作- (上面的新版本)中的$!
会这样做。