4

我想编写一个 Web 服务器,将其状态存储在一个带有/的State单子中。像这样的东西:waiwarp

{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.Wai.Handler.Warp
import Network.HTTP.Types
import Control.Monad.State
import Data.ByteString.Lazy.Char8

main = run 3000 app

text x = responseLBS
        status200
        [("Content-Type", "text/plain")]
    x

app req = return $ text "Hello World"

app1 req = modify (+1) >>= return . text . pack . show

-- main1 = runStateT (run 3000 app1) 0

当然,注释行不起作用。目的是将计数器存储在 state monad 中,并在每次请求时显示其递增的值。

另外,我如何获得线程安全?warp 是顺序还是并行运行我的中间件?

该州有哪些可用选项 - 除了IORef我可以在这种情况下使用之外,还有什么可以使用的吗?

我知道国家提供安全,但似乎 wai 不允许国家。

我只需要一个可以从其他地方调用的非常简单的单线程 RPC。Haxr包需要一个单独的 Web 服务器,这是一个矫枉过正。请参阅从 Node.JS 调用 Haskell - 它没有任何建议,因此我使用 Wai/Warp 和 Aeson 编写了一个简单的服务器。但似乎 WAI 旨在支持并发实现,因此它使事情变得复杂。

4

2 回答 2

5

如果它在Statemonad 中,则它在设计上是线程安全的。没有可能的共享状态的并发 IO 操作。它要么是线程安全的,要么无法编译。

如果作为设计的一部分,您可以真正并行访问共享状态(即更新全局计数器的单独 forkIO 线程),那么您需要在monadMVar (或其他一些事务屏障)中使用和或或以确保原子性。TVarSTM

于 2012-05-04T13:37:52.660 回答
3

如果您与状态的交互可以通过一次调用来表达atomicModifyIORef,您可以使用它,并且您不需要显式地序列化对状态的访问。

import Data.IORef

main = do state <- newIORef 42
          run 3000 (app' state)

app' :: IORef Int -> Application
app' ref req
   = return . text . pack . show `liftM` atomicModifyIORef ref (\st -> (st + 1, st + 1))

如果您的交互更复杂,并且您需要强制执行请求的完全序列化,您可以将 anMVarStateT.

import Control.Concurrent.MVar
import Control.Monad.State.Strict

main = do state <- newMVar 42
          run 3000 (app' state)

app' :: MVar Int -> Application
app' ref request
   = do state <- takeMVar ref
        (response, newState) <- runStateT (application request) state
        putMVar newState --TODO: ensure putMVar happens even if an exception is thrown
        return response

application :: Request -> StateT Int (ResourceT IO) Response
application request = modify (+1) >>= return . text . pack . show
于 2012-05-04T15:47:44.823 回答