Sea*_*ess 8 monads haskell monad-transformers scotty
我正在尝试制作一个存储数据的愚蠢的网络服务器State.我正在使用Web.Scotty.我之前使用过ReaderT和scotty来访问配置,但是遵循相同的方法在这里不起作用.它会根据每个请求重置状态.
我想在程序启动时设置初始状态,然后在程序的整个生命周期中保持相同的状态.
我怎样才能做到这一点?(以下为每个请求创建一个新状态)
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty.Trans
import Control.Monad.State (StateT, evalStateT, lift)
import qualified Control.Monad.State as S
import Data.Text.Lazy (Text)
main :: IO ()
main = do
let runner = flip evalStateT "message"
scottyT 3000 runner runner routes
routes :: ScottyT Text (StateT Text IO) ()
routes = do
get "/data" $ do
val <- lift S.get
text val
put "/data/:val" $ do
val <- param "val"
lift $ S.put val
text val
Run Code Online (Sandbox Code Playgroud)
您看到的行为绝对是预期的行为:请注意文档中scottyT第三个参数的注释:
-> (m Response -> IO Response)-运行单子m到IO,所谓的每一个动作.
你可以做的是将状态存储在StateTmonad 外部,以便你可以在每个动作的处理程序中恢复它.我能想到的最天真的方式是这样的:
main :: IO ()
main = do
let s0 = "message"
let transform = flip evalStateT s0
runner <- restartableStateT s0
scottyT 3000 transform runner routes
restartableStateT :: s -> IO (StateT s IO a -> IO a)
restartableStateT s0 = do
r <- newIORef s0
return $ \act -> do
s <- readIORef r
(x, s') <- runStateT act s
atomicModifyIORef' r $ const (s', x)
Run Code Online (Sandbox Code Playgroud)
但这并没有真正解决如果两个请求同时进入会发生什么,它只是"最后一个完成胜利".