2015-08-11 5 views
1

Je commence juste avec haskell et j'ai des problèmes avec un serveur REST de base "echo". Spock ressemblait à un bon endroit de départ pour un serveur REST, et je pensais avoir les bases de la monade d'État, mais je n'arrive pas à comprendre comment mettre un runState autour du code spock.Comment utiliser une monade d'état persistante avec Spock?

Voici le code que j'ai jusqu'à maintenant.

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

La clé de ce puzzle est le premier argument de 'spockT', que vous devrez fournir pour' m ~ State String'. Cependant, vous rencontrerez exactement le même problème que celui expliqué [dans cette réponse] (http://stackoverflow.com/a/27496393/477476): la chaîne 'State String' ne sera pas automagiquement conservée entre les appels de gestionnaire. – Cactus

Répondre

2

OK alors voici une version du restartableStateT bidouille pour votre exemple:

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

Comme l'autre réponse, celui-ci crée une seule IORef globale pour stocker « l'état ». Le runner passé à spockT est alors capable d'exécuter n'importe quel calcul StateT String IO en obtenant l'état à partir de ce IORef, en exécutant le calcul et en remettant l'état résultant dans le IORef.

Je voudrais réitérer de l'autre réponse que ce n'est pas nécessairement une bonne idée, car il n'a pas d'histoire pour la concurrence. Je suppose que cela pourrait être évoqué en utilisant STM par exemple, mais ... Je pense que vous devriez simplement utiliser une base de données pour ce genre de chose.

+0

Normalement, je voudrais juste utiliser une base de données, mais c'était juste pour essayer. Merci pour l'information, maintenant j'ai un tas de choses à apprendre. – ReaperUnreal