我如何在 Spock 中使用持久状态 Monad?
How do I use a persistent State monad with Spock?
我刚开始使用 haskell,我遇到了基本 "echo" REST 服务器的问题。
Spock 看起来是一个不错的 REST 服务器起点,虽然我了解了 State monad 的基础知识,但我在理解如何在 spock 代码周围放置 runState
时遇到了问题。
这是我目前得到的代码。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Monoid
import Web.Spock.Safe
import qualified Control.Monad.State as S
storeData :: String -> S.State String String
storeData val = do S.put val
return val
getData :: S.State String String
getData = do val <- S.get
return val
main :: IO ()
main =
runSpock 11350 $ spockT id $
do get "store" $
text "Would be a call to getData"
好的,下面是 restartableStateT
hack 的一个版本:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
module Main where
import Data.Monoid
import Data.String (fromString)
import Web.Spock.Safe
import qualified Control.Monad.State as S
import Data.IORef
storeData :: (Monad m) => String -> S.StateT String m String
storeData val = do S.put val
return val
getData :: (Monad m) => S.StateT String m String
getData = do val <- S.get
return val
newtype RunStateT s m = RunStateT{ runStateT :: forall a. S.StateT s m a -> m a }
restartableStateT :: s -> IO (RunStateT s IO)
restartableStateT s0 = do
r <- newIORef s0
return $ RunStateT $ \act -> do
s <- readIORef r
(x, s') <- S.runStateT act s
atomicModifyIORef' r $ const (s', x)
main :: IO ()
main = do
runner <- restartableStateT "initial state"
runSpock 11350 $ spockT (runStateT runner) $ do
get "store" $ do
cmd <- param "value"
case cmd of
Nothing -> do
old <- S.lift getData
text $ fromString old
Just new -> do
S.lift $ storeData new
text "Stored."
与其他答案一样,这个答案创建了一个全局 IORef
来存储 "the state"。传递给 spockT
的 runner
然后能够 运行 任何 StateT String IO
计算,通过从这个 IORef
、运行 计算中获取状态,并将结果状态放回 IORef
。
我想从另一个答案中重申,这不一定是个好主意,因为它没有并发的故事。我想这可以通过使用 STM 来掩盖,但是......我认为你应该只使用数据库来处理这种事情。
我刚开始使用 haskell,我遇到了基本 "echo" REST 服务器的问题。
Spock 看起来是一个不错的 REST 服务器起点,虽然我了解了 State monad 的基础知识,但我在理解如何在 spock 代码周围放置 runState
时遇到了问题。
这是我目前得到的代码。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Monoid
import Web.Spock.Safe
import qualified Control.Monad.State as S
storeData :: String -> S.State String String
storeData val = do S.put val
return val
getData :: S.State String String
getData = do val <- S.get
return val
main :: IO ()
main =
runSpock 11350 $ spockT id $
do get "store" $
text "Would be a call to getData"
好的,下面是 restartableStateT
hack 的一个版本:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
module Main where
import Data.Monoid
import Data.String (fromString)
import Web.Spock.Safe
import qualified Control.Monad.State as S
import Data.IORef
storeData :: (Monad m) => String -> S.StateT String m String
storeData val = do S.put val
return val
getData :: (Monad m) => S.StateT String m String
getData = do val <- S.get
return val
newtype RunStateT s m = RunStateT{ runStateT :: forall a. S.StateT s m a -> m a }
restartableStateT :: s -> IO (RunStateT s IO)
restartableStateT s0 = do
r <- newIORef s0
return $ RunStateT $ \act -> do
s <- readIORef r
(x, s') <- S.runStateT act s
atomicModifyIORef' r $ const (s', x)
main :: IO ()
main = do
runner <- restartableStateT "initial state"
runSpock 11350 $ spockT (runStateT runner) $ do
get "store" $ do
cmd <- param "value"
case cmd of
Nothing -> do
old <- S.lift getData
text $ fromString old
Just new -> do
S.lift $ storeData new
text "Stored."
与其他答案一样,这个答案创建了一个全局 IORef
来存储 "the state"。传递给 spockT
的 runner
然后能够 运行 任何 StateT String IO
计算,通过从这个 IORef
、运行 计算中获取状态,并将结果状态放回 IORef
。
我想从另一个答案中重申,这不一定是个好主意,因为它没有并发的故事。我想这可以通过使用 STM 来掩盖,但是......我认为你应该只使用数据库来处理这种事情。