我想创建一个具有大量数据库访问权限的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
使用:
getItemList :: MyDBMonad (Error [Item])
getItemList = do
-- etc...
但是我对Monad和Monad变形金刚知之甚少(我把这个问题视为一个练习来了解它),我不知道如何开始创建Database Monad,如何将IO从happstack提升到数据库堆栈等......
答案 0 :(得分:6)
您可能想要使用'ReaderT':
type MyMonad a = ReaderT DbHandle ServerPart a
Reader
monad转换器使用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
更改为LogHandle
,无论是什么。
一旦你获得了两件以上的东西,你就可以获得一个合适的记录类型而不是一个元组。
答案 1 :(得分:6)
这是一些从上面的片段编译的最小工作代码,用于像我这样的混淆新手。
您将内容放入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"]}