如何在Happstack中创建数据库Monad堆栈



我想创建一个可以大量访问数据库的Happstack应用程序。我认为一个在底部有IO的Monad Stack和一个在顶部有类似数据库写的Monad(在中间有日志写入器(将在每次访问中都有一个清晰的函数,例如:

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

带有:

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

但我对Monad和Monad Transformers知之甚少(我把这个问题看作是一个学习它的练习(,我不知道如何开始创建数据库Monad,如何将IO从happstack提升到数据库堆栈,。。。等等

下面是一些从上面的代码片段编译而来的最低限度的工作代码,供像我这样困惑的新手使用

你把东西放进AppConfig类型,然后在你的反应器里用ask抓取。

{-# 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"]}

您可能想要使用"ReaderT":

type MyMonad a = ReaderT DbHandle ServerPart a

Reader monad转换器使用ask函数可以访问单个值——在这种情况下,我们希望每个人都能访问的值是数据库连接。

在这里,DbHandle是与数据库的某种连接。

因为"ReaderT"已经是所有happstack服务器类型类的实例,所以所有正常的happstack server函数都将在此monad中工作。

您可能还需要某种帮助程序来打开和关闭数据库连接:

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

(在这里使用像"bracket"这样的函数可能更好,但我不知道ServerPart monad是否有这样的操作(

我不知道你想如何进行日志记录——你计划如何与你的日志文件交互?类似于:

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

然后:

askDb :: MyMonad DbHandle
askDb = fst <$> ask
askLogger :: MyMonad LogHandle
askLogger = snd <$> ask

也许就足够了。然后,您可以在这些基元的基础上构建更高级别的函数。您还需要更改runMyMonad以在LogHandle中传递,不管是什么

一旦您获得了两个以上想要访问的内容,那么拥有一个合适的记录类型而不是元组是值得的。

相关内容

  • 没有找到相关文章

最新更新