2011-10-18 11 views
5

Voglio creare un'applicazione Happstack con un sacco di accesso a un database. Penso che una pila Monade con IO in basso e un database Write-come monade in alto (con lo scrittore registro al centro) lavorerà per avere una chiara funzione di ogni accesso, ad esempio:Come creare uno stack del database Monad in Happstack?

itemsRequest :: ServerConfig -> ServerPart Response 
itemsRequest cf = dir "items" $ do 
    methodM [GET,HEAD] 
    liftIO $ noticeM (scLogger cf) "sended job list" 

    items <- runDBMonad (scDBConnString cf) $ getItemLists 

    case items of 
    (Right xs) -> ok $ toResponse $ show xs 
    (Left err) -> internalServerError $ toResponse $ show err 

Con:

getItemList :: MyDBMonad (Error [Item]) 
getItemList = do 
    -- etc... 

Ma ho poca conoscenza di Monade e Monade Transformers (vedo questa domanda come un esercizio per imparare a questo proposito), e non ho idea di come iniziare la creazione di database Monade, come sollevare il iO da happstack allo stack del database, ecc.

+0

Ho provato a utilizzare 'unsafePerformIO' per eseguire l'I/O. come l'Happstack usa una combinazione puramente, forse è il tuo unico modo di fare IO. – Nybble

+0

@Wu Xingbo, E 'possibile fare IO su happstack con liftIO, ma non so chi passare ad un altro stack di monad. – Zhen

risposta

6

È probabile che desidera utilizzare 'ReaderT':

type MyMonad a = ReaderT DbHandle ServerPart a 

Il trasformatore monade Reader fa un singolo valore accessibili tramite la funzione ask - in questo caso, il valore che vogliamo tutti di ottenere a è la connessione al database .

Qui, DbHandle c'è qualche connessione al database.

Poiché "ReaderT" è già un'istanza di tutte le classi di tipi di server happstack, tutte le normali funzioni del server happstack funzioneranno in questa monade.

Probabilmente si desidera anche una sorta di aiuto per aprire e chiudere la connessione al database:

runMyMonad :: String -> MyMonad a -> ServerPart a 
runMyMonad connectionString m = do 
    db <- liftIO $ connect_to_your_db connectionString 
    result <- runReaderT m db 
    liftIO $ close_your_db_connection db 

(Potrebbe essere meglio utilizzare una funzione come 'staffa' qui, ma non so che ci è una tale operazione per la monade ServerPart)

Non so come si desidera eseguire la registrazione: come si intende interagire con il file di registro? Qualcosa di simile:

type MyMonad a = ReaderT (DbHandle, LogHandle) ServerPart a 

e poi:

askDb :: MyMonad DbHandle 
askDb = fst <$> ask 

askLogger :: MyMonad LogHandle 
askLogger = snd <$> ask 

potrebbe essere sufficiente. Potresti quindi costruire su quelle primitive per creare funzioni di livello superiore. Dovresti anche cambiare runMyMonad da passare in un LogHandle, qualunque esso sia.

Una volta ottenuti più di due elementi a cui si desidera accedere, è necessario disporre di un tipo di record corretto anziché di una tupla.

+2

Argomento laterale: per il pooling delle connessioni c'è http://hackage.haskell.org/package/resource-pool e http://hackage.haskell.org/package/pool. Potrebbe essere più del necessario, però. –

+0

grazie per l'informazione sulla piscina! – Zhen

6

Ecco un codice di lavoro minimo compilato da frammenti sopra per principianti confusi come me.

Inserisci le cose nel tipo AppConfig e prendili con ask all'interno dei responsabili delle risposte.

{-# LANGUAGE OverloadedStrings #-} 
module Main where 

import Happstack.Server 
import Control.Monad.Reader 
import qualified Data.ByteString.Char8 as C 

myApp :: AppMonad Response 
myApp = do 
    -- access app config. look mom, no lift! 
    test <- ask 

    -- try some happstack funs. no lift either. 
    rq <- askRq 
    bs <- lookBS "lol" 

    -- test IO please ignore 
    liftIO . print $ test 
    liftIO . print $ rq 
    liftIO . print $ bs 

    -- bye 
    ok $ toResponse ("Oh, hi!" :: C.ByteString) 

-- Put your stuff here. 
data AppConfig = AppConfig { appSpam :: C.ByteString 
          , appEggs :: [C.ByteString] } deriving (Eq, Show) 
config = AppConfig "THIS. IS. SPAAAAAM!!1" [] 

type AppMonad = ReaderT AppConfig (ServerPartT IO) 

main = simpleHTTP (nullConf {port=8001}) $ runReaderT myApp config {appEggs=["red", "gold", "green"]} 
Problemi correlati