1

我正在实现简单的 http 服务器,我希望我的响应取决于一些全局状态。例如,如果我第一次从同一个客户端收到请求“get_settings”,我将发送大型设置 json,而第二次我将发送“未修改”http 响应。

类似的东西

import Network.Simple.TCP

main = withSocketsDo $ do 
    let settings_state = 0 -- flag for settings response
    serve (Host "127.0.0.1") "23980" $ \(conn_sock, remote_addr) -> do
        putStrLn $ "TCP connection established from " ++ show remote_addr
        (Just inp) <- recv conn_sock 1024
        send conn_sock (process inp settings_state)

process :: B.ByteString -> Int -> B.ByteString
process inp flag
    | flag == 0 = ... -- return full response and change global flag
    | otherwise = ... -- return 'Not-modified'

问题是我该如何实现它?我想尽可能简单地手动完成,没有任何 Monad Transformers 等等。让代码丑陋,但简单。谢谢

4

1 回答 1

4

由于更改标志显然有一些副作用,结果process将是IO

process :: B.ByteString -> Int -> IO B.ByteString

由于您不想使用任何类型的 monad 转换器,因此您需要Int使用一些可变引用来交换 。是的,您没看错:有几种类型可变的,例如IORef, MVar, TVar, MVector, STRef...。为了保持简单,让我们坚持IORef.

process :: B.ByteString -> IORef Int -> IO B.ByteString
process inp flag = do
    oldFlag <- readIORef flag
    if oldFlag == 0 
       then do modifyIORef' flag (+1)
               return bigJSONObject
       else return notModified

请注意,您没有为标志提供任何逻辑,因此我只是增加了该值,但您可能想做其他事情(或将标志更改为IORef Bool)。请注意,atomicModifyIORef'如果您想IORef在多线程程序中安全地使用,您还需要使用:

    oldFlag <- atomicModifyIORef' flag (\o -> (o+1,o))

无论哪种方式,您都需要创建IORefwith newIORef value,因此您的代码片段类似于

main = withSocketsDo $ do 
    settings_state <- newIORef 0
    serve (Host "127.0.0.1") "23980" $ \(conn_sock, remote_addr) -> do
        -- ...
于 2014-07-19T21:08:50.283 回答