5

我想创建一个可以大量访问数据库的 Happstack 应用程序。我认为底部带有 IO 的 Monad 堆栈和顶部的类似数据库写入的 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 知之甚少(我将这个问题视为学习它的练习),并且我不知道如何开始创建 Database Monad,如何将 IO 从 happstack 提升到 Database Stack, ...ETC。

4

2 回答 2

7

这是一些从上面的片段编译的最小工作代码,供像我这样困惑的新手使用。

你把东西输入类型,然后在你的响应器中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"]}
于 2012-06-21T13:44:14.143 回答
6

您可能想使用“ReaderT”:

type MyMonad a = ReaderT DbHandle ServerPart a

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

DbHandle是与您的数据库的一些连接。

因为 'ReaderT' 已经是所有 happstack-server 类型类的实例,所以所有普通的 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以传入 a LogHandle,无论是什么。

一旦你得到两个以上的东西,你想要访问它就需要有一个正确的记录类型而不是一个元组。

于 2011-10-19T12:32:37.767 回答