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

Zhe*_*hen 5 monads haskell monad-transformers happstack

我想创建一个Happstack应用程序,它具有对数据库的大量访问权限.我认为底部有IO的Monad Stack和顶部的Database Write-like 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
Run Code Online (Sandbox Code Playgroud)

附:

getItemList :: MyDBMonad (Error [Item])
getItemList = do
  -- etc...
Run Code Online (Sandbox Code Playgroud)

但我对Monad和Monad变形金刚知之甚少(我把这个问题视为一个练习来了解它),我不知道如何开始创建Database Monad,如何将IO从happstack提升到数据库堆栈, ...等等.

wiz*_*wiz 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"]}
Run Code Online (Sandbox Code Playgroud)


Ant*_*ter 6

您可能想要使用'ReaderT':

type MyMonad a = ReaderT DbHandle ServerPart a
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

(这里使用像'bracket'这样的函数可能会更好,但我不知道ServerPart monad有这样的操作)

我不知道你想如何做日志记录 - 你打算如何与你的日志文件进行交互?就像是:

type MyMonad a = ReaderT (DbHandle, LogHandle) ServerPart a
Run Code Online (Sandbox Code Playgroud)

然后:

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

askLogger :: MyMonad LogHandle
askLogger = snd <$> ask
Run Code Online (Sandbox Code Playgroud)

可能就够了.然后,您可以构建这些基元以生成更高级别的函数.您还需要更改runMyMonad为传递给a LogHandle,无论是什么.

一旦你获得了超过两件你想要访问的东西,就需要有一个合适的记录类型而不是一个元组.

  • 附加话题:对于连接池,有http://hackage.haskell.org/package/resource-pool和http://hackage.haskell.org/package/pool.但这可能比你需要的还要多. (2认同)