如何在Spock中使用持久性State monad? [英] How do I use a persistent State monad with Spock?

查看:110
本文介绍了如何在Spock中使用持久性State monad?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是从haskell开始,而基本的"echo" REST服务器却遇到了问题.

I'm just starting out with haskell and I'm having issues with a basic "echo" REST server.

Spock看起来像是REST服务器的一个不错的起点,尽管我了解了State monad的基础知识,但是在理解如何在spock代码周围放置runState时遇到了问题.

Spock looked like a nice starting place for a REST server, and I though I got the basics of the State monad, but I'm having issues understanding how to put a runState around the spock code.

这是我到目前为止获得的代码.

Here's the code I've got so far.

{-# 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 以您的示例为例:

OK so here's a version of the restartableStateT hack for your example:

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

Like the other answer, this one creates a single global IORef to store "the state". The runner passed to spockT is then able to run any StateT String IO computation by getting the state from this IORef, running the computation, and putting the resulting state back into the IORef.

我想从另一个答案中重申,这不一定是一个好主意,因为它没有并发的故事.我想可以通过使用STM等方式将其记录下来,但是...我认为您应该只将数据库用于此类事情.

I would like to reiterate from the other answer that this is not necessarily a good idea, because it has no story for concurrency. I guess that could be papered over by using STM for example, but... I think you should just use a database for this kind of thing.

这篇关于如何在Spock中使用持久性State monad?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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