【问题标题】:How do I use a persistent State monad with Spock?如何在 Spock 中使用持久的 State monad?
【发布时间】:2015-08-11 22:06:37
【问题描述】:

我刚开始使用 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"

【问题讨论】:

  • 这个谜题的关键是spockT 的第一个参数,你需要为m ~ State String 提供它。但是,您将遇到与 in this answer 解释的完全相同的问题:State String 不会在处理程序调用之间自动持久化。

标签: rest haskell monads state-monad haskell-spock


【解决方案1】:

好的,下面是 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 来存储“状态”。然后,传递给spockTrunner 能够运行任何StateT String IO 计算,方法是从此IORef 获取状态、运行计算并将结果状态放回IORef

我想从另一个答案中重申,这不一定是一个好主意,因为它没有关于并发的故事。我想这可以通过使用 STM 来解决,但是......我认为你应该只使用数据库来处理这种事情。

【讨论】:

  • 通常我只会使用数据库,但这只是为了尝试。谢谢你的信息,现在我有很多东西要学。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-20
  • 1970-01-01
  • 1970-01-01
  • 2019-10-29
  • 1970-01-01
相关资源
最近更新 更多