在Haskell Servant应用程序中启动应用程序启动时间间隔 [英] Setting off a interval on application launch in a Haskell Servant app
问题描述
我试图为使用Servant的基于浏览器的游戏构建后端,我希望有一种游戏循环让我每隔< IORef
中包含了一些游戏状态,并且作为尝试获取某些内容的初始尝试,我试图每2秒更新一次状态值。这里是我的:
{ - #LANGUAGE DataKinds# - }
{ - #LANGUAGE DeriveGeneric# - }
{ - #LANGUAGE FlexibleInstances# - }
{ - #LANGUAGE MultiParamTypeClasses# - }
{ - #LANGUAGE OverloadedStrings# - }
{ - #LANGUAGE TypeOperators# - }
模块Main其中
导入Prelude()
导入Prelude.Compat
导入Control.Concurrent(forkIO,threadDelay)
导入Control.Monad(永久)
导入Control.Monad.Reader
导入Data.Aeson.Compat
导入Data.Aeson.Types
导入Data.Maybe
导入Data.IORef
import GHC.Generics
import Network.Wai.Handler.Warp
import Servant
import Servant.Utils.StaticFiles(serveDirectory)
键入Api =players:>获取'[JSON] [Player]
:< |> tick:>获取'[JSON]整数
类型游戏= Api:< |>原始
数据播放器=播放器
{名称::字符串
}派生(Eq,Show,Generic)
实例ToJSON播放器
data Action = AddPlayer Player
| Tick
data State = State {
players :: [Player]
,tick :: Integer}
initialState :: State
initialState = State {players = []
,tick = 0
}
update :: Action - >状态 - >状态
更新动作状态=
案例动作
AddPlayer p - >
state {players = [p] ++(players state)}
Tick - >
状态{tick = 1 +(tick状态)}
updateState :: Action - > IORef状态 - > IO State
updateState action state =
atomicModifyIORef state(\s - >(next s,s))
where next =更新动作
seconds :: Int - > Int
秒=(* 1000000)
getPlayers :: IORef状态 - > Handler [Player]
getPlayers state = liftIO $ do
_< - updateState(AddPlayer $ PlayerMe)state
s< - readIORef state
return $ players s
getTick :: IORef状态 - > Handler Integer
getTick state = liftIO $ do
s< - readIORef state
return $ tick s
everything :: Proxy Game
everything = Proxy
server :: IORef状态 - >服务器游戏
服务器状态=(getPlayers状态
:< |> getTick状态)
:< |> serveDirectoryFileServer./build
app :: IORef状态 - >应用程序
app state =提供所有内容(服务器状态)
$ b $ main main :: IO()
main = do
let port = 8000
state = newIORef initialState
threadId< - forkIO $ forever $ do
threadDelay $ seconds 2
return $ updateState Tick =<<状态
putStrLn $在++ show port
run port上运行服务器。 app =<<状态
应用程序可以构建,但它不会按照我想要的那样访问 / tick
总是返回 0
。我猜这是要改变状态在单独的线程中发生,还是在两个不同的时间传递 IO
值?不过我相信 forkIO
必须发生在 IO
块中,所以我不确定如何获得这两个值见面。
这种事情正是Haskell试图避免的,这可能是为什么它很难实现。我的问题是,我想要一些方法来触发一个函数(可以修改 State
)每个 x
秒,如果解决方案涉及到一个完全独立的路线,那就这样吧。
main :: IO()
main = do
let port = 8000
ref< - newIORef initialState
threadId< - forkIO $ forever $ do
threadDelay $秒2
updateState状态ref
运行端口$ app ref
I'm trying to build the backend for a browser based game using Servant, I want to have some kind of game loop that lets me fire out requests every x
seconds. I already have some game state contained in an IORef
, and as an initial attempt to get something working I am trying to update my state value every 2 seconds. Here is what I have:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Prelude ()
import Prelude.Compat
import Control.Concurrent(forkIO, threadDelay)
import Control.Monad(forever)
import Control.Monad.Reader
import Data.Aeson.Compat
import Data.Aeson.Types
import Data.Maybe
import Data.IORef
import GHC.Generics
import Network.Wai.Handler.Warp
import Servant
import Servant.Utils.StaticFiles (serveDirectory)
type Api = "players" :> Get '[JSON] [Player]
:<|> "tick" :> Get '[JSON] Integer
type Game = Api :<|> Raw
data Player = Player
{ name :: String
} deriving (Eq, Show, Generic)
instance ToJSON Player
data Action = AddPlayer Player
| Tick
data State = State {
players :: [Player]
, tick :: Integer }
initialState :: State
initialState = State { players = []
, tick = 0
}
update :: Action -> State -> State
update action state =
case action of
AddPlayer p ->
state { players = [p] ++ (players state) }
Tick ->
state { tick = 1 + (tick state) }
updateState :: Action -> IORef State -> IO State
updateState action state =
atomicModifyIORef state (\s -> (next s, s))
where next = update action
seconds :: Int -> Int
seconds = (* 1000000)
getPlayers :: IORef State -> Handler [Player]
getPlayers state = liftIO $ do
_ <- updateState (AddPlayer $ Player "Me") state
s <- readIORef state
return $ players s
getTick :: IORef State -> Handler Integer
getTick state = liftIO $ do
s <- readIORef state
return $ tick s
everything :: Proxy Game
everything = Proxy
server :: IORef State -> Server Game
server state = (getPlayers state
:<|> getTick state)
:<|> serveDirectoryFileServer "./build"
app :: IORef State -> Application
app state = serve everything (server state)
main :: IO ()
main = do
let port = 8000
state = newIORef initialState
threadId <- forkIO $ forever $ do
threadDelay $ seconds 2
return $ updateState Tick =<< state
putStrLn $ "Running server on " ++ show port
run port . app =<< state
The app builds, but it doesn't do what I want it to, visiting /tick
always returns 0
. I'm guessing this is either something to do with the change to state happening in a separate thread, or the IO
value being passed in two separate times? However I believe that forkIO
has to happen inside an IO
block, so I'm unsure how to get the two values to meet.
This kind of thing is exactly what Haskell seeks to avoid, which is probably why it's so difficult to achieve. My problem is that I want to have some way to trigger a function (that is able to modify State
) every x
seconds, if the solution involves going down an entirely separate route then so be it.
state
creates new IORef every time. Your web server and your thread update function work on two different IORefs and therefore on two different states. You want to share the IORef. Something like the following should work.
main :: IO ()
main = do
let port = 8000
ref <- newIORef initialState
threadId <- forkIO $ forever $ do
threadDelay $ seconds 2
updateState state ref
run port $ app ref
这篇关于在Haskell Servant应用程序中启动应用程序启动时间间隔的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!