在Web.Scotty中使用StateT [英] Use StateT within Web.Scotty

查看:104
本文介绍了在Web.Scotty中使用StateT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图制作一个愚蠢的网络服务器,将数据存储为 State 。我正在使用 Web.Scotty 我之前用scotty使用过ReaderT来访问配置,但遵循相同的方法在这里不起作用。它会在每个请求中重置状态。

我想在程序启动时设置初始状态,然后在程序的整个生命周期中保持同样的状态。



我该如何做这项工作? (以下每个请求都会创建一个新的状态)

$ $ p $ $ $ c $ {$#$ L $ $ b $ import import Web.Scotty .Trans
导入Control.Monad.State(StateT,evalStateT,lift)
将合格的Control.Monad.State导入为S
import Data.Text.Lazy(Text)

main :: IO()
main = do
let runner = flip evalStateTmessage
scottyT 3000 runner runner routes

routes :: ScottyT Text (StateT Text IO)()
routes = do
$ b $ get/ data$ do
val< - lift S.get
text val

放入/ data /:val$ do
val < - paramval
lift $ S.put val
text val


解决方案

您看到的行为绝对是预期的行为:
注释请参阅文档中的第三个参数对于 scottyT


- > (m响应 - > IO响应) - 将monad m 运行为 IO

您可以将状态存储在 StateT monad,以便您可以将其恢复到每个操作的处理程序中。我能想到的最简单的方式就是这样:

  main :: IO()
main = do
let s0 =message
let transform = flip evalStateT s0
runner< - restartableStateT s0
scottyT 3000变换跑步者路线

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)

但是这并不能解决两个请求同时发生时应该发生的情况,它只是最后一个赢得胜利。


I'm trying to make a silly webserver that stores data as State. I'm using Web.Scotty. I've used ReaderT before with scotty to access config, but following the same approach doesn't work here. It resets the state on every request.

I want to set the initial state when the program starts, then have that same state stick around for the whole life of the program.

How can I make this work? (The following creates a new state every request)

{-# 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

解决方案

The behaviour you are seeing is definitely the expected one: note the remark on the third argument in the documentation for scottyT:

-> (m Response -> IO Response) -- Run monad m into IO, called at each action.

What you could do is store the state external to the StateT monad so that you can reinstate it in the handler of every action. The most naïve way I can think of to do that would be something like this:

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)

but this doesn't really address what should happen if two requests are coming in concurrently, it's just "last one to finish wins".

这篇关于在Web.Scotty中使用StateT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆