2014-12-16 19 views
8

Sto provando a creare un server Web stupido che memorizza i dati come State. Sto usando Web.Scotty. I've used ReaderT before with scotty to access config, ma seguire lo stesso approccio non funziona qui. Reimposta lo stato su ogni richiesta.Usa StateT all'interno di Web.Scotty

Desidero impostare lo stato iniziale all'avvio del programma, quindi mantenere lo stesso stato per tutta la durata del programma.

Come posso fare questo lavoro? (Di seguito crea un nuovo stato ogni richiesta)

{-# LANGUAGE OverloadedStrings #-} 
import Web.Scotty.Trans 
import Control.Monad.State (StateT, evalStateT, lift) 
import qualified Control.Monad.State as S 
import Data.Text.Lazy (Text) 

main :: IO() 
main = do 
    let runner = flip evalStateT "message" 
    scottyT 3000 runner runner routes 

routes :: ScottyT Text (StateT Text IO)() 
routes = do 

    get "/data" $ do 
    val <- lift S.get 
    text val 

    put "/data/:val" $ do 
    val <- param "val" 
    lift $ S.put val 
    text val 
+2

Anche nei server Web scritti in lingue completamente non funzionanti (apache, tomcat, ecc.), In genere non si memorizza lo stato condiviso nella memoria .... È necessario preoccuparsi del blocco, dello stato di archiviazione all'arresto, mappare i dati agli utenti, ecc. Questo è il motivo per cui esistono database ... Suppongo che non si possa fare ciò che si vuole fare in Scotty, e gli autori di Scotty non vorrebbero implementarlo (ma questo è solo la mia ipotesi). – jamshidh

+0

Questo ha senso. L'unica ragione per cui sto facendo questo è insegnare ad alcune persone che haskell proviene da node.js e mi è sembrato un primo passo facile prima di inserire un database. Indovinate comunque :) –

risposta

6

Il comportamento che state vedendo è sicuramente quello atteso: nota il commento sul terzo argomento nella documentation for scottyT:

-> (m Response -> IO Response) - Run monad m in IO, chiamato ad ogni azione.

Che cosa si potrebbe fare è memorizzare lo stato esterno al StateT monade, in modo da poter ripristinare nel gestore di ogni azione. Il modo più ingenuo che posso pensare di farlo sarebbe qualcosa di simile:

main :: IO() 
main = do 
    let s0 = "message" 
    let transform = flip evalStateT s0 
    runner <- restartableStateT s0 
    scottyT 3000 transform runner routes 

restartableStateT :: s -> IO (StateT s IO a -> IO a) 
restartableStateT s0 = do 
    r <- newIORef s0 
    return $ \act -> do 
     s <- readIORef r 
     (x, s') <- runStateT act s 
     atomicModifyIORef' r $ const (s', x) 

, ma questo in realtà non affrontare quello che dovrebbe accadere se due richieste stanno arrivando in concomitanza, è solo "ultimo a finire vittorie ".

+0

vale la pena notare che c'è un esempio nel documenti scotty per questo esatto scenario https://github.com/scotty-web/scotty/blob/master/examples/globalstate.hs – fommil