2015-08-11 3 views
1

Я только начинаю с haskell, и у меня возникают проблемы с базовым сервером REST «echo».Как использовать постоянную государственную монаду со Спок?

Спок выглядел как приятное стартовое место для сервера REST, и, хотя я получил основы государственной монады, но у меня возникли проблемы с пониманием того, как поставить 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" 
+1

Ключом к этой головоломке является первый аргумент 'spockT', который вам нужно будет предоставить для' m ~ State String'. Тем не менее, вы столкнетесь с той же проблемой, что объясняется [в этом ответе] (http://stackoverflow.com/a/27496393/477476): 'State String' не будет автоматически сохраняться между вызовами обработчика. – Cactus

ответ

2

ИТАК вот версия restartableStateT хака для вашего примера:

{-# 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 хранить «состояние». Затем runner, прошедший до spockT, может выполнить любое вычисление StateT String IO, получив состояние из этого IORef, выполнив вычисление и вернув полученное состояние обратно в IORef.

Я хотел бы повторить из другого ответа, что это не обязательно хорошая идея, потому что у нее нет истории для параллелизма. Я думаю, что это можно было бы перепечатать с помощью STM, например, но ... Я думаю, вы должны просто использовать базу данных для такого рода вещей.

+0

Обычно я бы просто использовал БД, но это было просто попробовать. Спасибо за информацию, теперь у меня есть куча вещей, чтобы учиться. – ReaperUnreal

Смежные вопросы