Haskell Scotty:连接池作为monad读取器
有数以万亿计的单子教程,包括读者,当你读到它的时候,它似乎都很清楚。但当你真的需要写作时,这就变成了另一回事 我从未使用过阅读器,只是在实践中从未使用过。所以我不知道该怎么做,尽管我读到了 我需要在Scotty中实现一个简单的数据库连接池,以便每个操作都可以使用该池。该池必须是“全局”的,并且可由所有操作功能访问。我读到这样做的方法是读者单子。如果还有其他方法,请告诉我 你能帮我看看如何正确地使用阅读器吗? 如果我用我自己的例子看看是如何做到的,我可能会学得更快Haskell Scotty:连接池作为monad读取器,haskell,scotty,haskell-wai,Haskell,Scotty,Haskell Wai,有数以万亿计的单子教程,包括读者,当你读到它的时候,它似乎都很清楚。但当你真的需要写作时,这就变成了另一回事 我从未使用过阅读器,只是在实践中从未使用过。所以我不知道该怎么做,尽管我读到了 我需要在Scotty中实现一个简单的数据库连接池,以便每个操作都可以使用该池。该池必须是“全局”的,并且可由所有操作功能访问。我读到这样做的方法是读者单子。如果还有其他方法,请告诉我 你能帮我看看如何正确地使用阅读器吗? 如果我用我自己的例子看看是如何做到的,我可能会学得更快 {-# LANGUAGE Ove
{-# LANGUAGE OverloadedStrings #-}
module DB where
import Data.Pool
import Database.MongoDB
-- Get data from config
ip = "127.0.0.1"
db = "index"
--Create the connection pool
pool :: IO (Pool Pipe)
pool = createPool (runIOE $ connect $ host ip) close 1 300 5
-- Run a database action with connection pool
run :: Action IO a -> IO (Either Failure a)
run act = flip withResource (\x -> access x master db act) =<< pool
{-#语言重载字符串}
模块数据库,其中
导入数据池
导入数据库.MongoDB
--从配置中获取数据
ip=“127.0.0.1”
db=“索引”
--创建连接池
池::IO(池管道)
pool=createPool(runIOE$connect$host ip)关闭1300 5
--使用连接池运行数据库操作
运行::操作IO a->IO(故障a)
运行act=flip with resource(\x->access x master db act)=addBase“static”)
获取“/json”$showJson
showJson::ActionT LT.Text(ReaderT(池管道)IO)()
showJson=do
p入口管道主“索引”a)
j正如您所提到的,使其可访问的方法是将您的计算封装在读取器
monad中,或者更可能是封装在读取器
转换器中。因此,您的运行
功能(略有更改)
但是您不希望在run
上使用runReaderT
,因为它可能是更大计算的一部分,也应该共享ReaderT
环境。尽量避免在“叶”计算中使用runReaderT
,通常应在程序逻辑中尽可能高的位置调用它
EDIT:Reader
和ReaderT
之间的区别在于Reader
是一个单子,而ReaderT
是一个单子转换器。也就是说,ReaderT
将Reader
行为添加到另一个monad(或monad转换器堆栈)。如果你不熟悉monad变形金刚,我推荐你
您有showJson-pool~ActionM()
,您想添加一个读卡器
环境,可以访问池管道
。在这种情况下,您实际上需要ActionT
和ScottyT
转换器,而不是ReaderT
,以便使用scotty
包中的函数
请注意,ActionM
定义为type ActionM=ActionT Text IO
,类似于ScottyM
我没有安装所有必要的库,因此这可能不会进行类型检查,但它应该会给您提供正确的想法
basal :: ScottyT Text (ReaderT (Pool Pipe) IO) ()
basal = do
middleware $ staticPolicy (...)
get "/json" showJson
showJson :: ActionT Text (ReaderT (Pool Pipe) IO) ()
showJson = do
pool <- lift ask
let run act = withResource pool (\p -> access p master "index act)
d <- liftIO $ run $ fetch $ select [] "tables"
text . TL.pack $ either (const "") show d
base::ScottyT文本(ReaderT(池管)IO)()
基本的
中间件$staticPolicy(…)
获取“/json”showJson
showJson::ActionT文本(ReaderT(池管道)IO)()
showJson=do
池访问(p主“索引法”)
d我一直在试图自己解决这个确切的问题。多亏了这个问题的提示和其他研究,我想出了以下对我有用的方法。你缺少的关键是使用scottyT
毫无疑问,有一种更漂亮的方式来编写runDB,但我在Haskell方面没有太多经验,所以如果你能做得更好,请发布它
type MCScottyM = ScottyT TL.Text (ReaderT (Pool Pipe) IO)
type MCActionM = ActionT TL.Text (ReaderT (Pool Pipe) IO)
main :: IO ()
main = do
pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5
scottyT 3000 (f pool) (f pool) $ app
where
f = \p -> \r -> runReaderT r p
app :: MCScottyM ()
app = do
middleware $ staticPolicy (noDots >-> addBase "public")
get "/" $ do
p <- runDB dataSources
html $ TL.pack $ show p
runDB :: Action IO a -> MCActionM (Either Failure a)
runDB a = (lift ask) >>= (\p -> liftIO $ withResource p (\pipe -> access pipe master "botland" a))
dataSources :: Action IO [Document]
dataSources = rest =<< find (select [] "datasources")
键入MCScottyM=ScottyT TL.Text(读取(池管)IO)
键入MCActionM=ActionT TL.Text(ReaderT(池管道)IO)
main::IO()
main=do
池\r->runReaderT r p
应用程序::MCScottyM()
app=do
中间件$staticPolicy(noDots>->addBase“public”)
获取“/”$do
p MCAction M(故障a中的任一个)
runDB a=(提升请求)>>=(\p->liftIO$withResource p(\pipe->access pipe master“botland”a))
数据源::操作IO[文档]
dataSources=rest=MCActionM(故障a)
runDB a=do
p非常感谢你的回答。很抱歉,我不明白。我试过了,但它在我的脑海中没有点击。我按照你的建议创建了run函数,但如果我不能掌握它,它是没有意义的复制和粘贴。一些示例说我需要使用变压器,一些示例说我只需要阅读器。有什么区别吗?我将更新我的问题显示了我在函数链中传递“pipe”变量的整个代码段。你能告诉我怎么做吗?我花了一个周末试图理解它。我知道它一定很简单,但它就是不点击。感谢你的更新,但不幸的是它不起作用。我试着不使用类型签名,希望GHC能我会扣除它并给我一些线索,但没有运气。谢谢你的答案和scottyT的建议。我几乎达到了这一点,减去scottyT。我用scottyT尝试了它,但仍然不起作用。我认为问题在于get
函数,它分别接受并返回旧的ActionM和ScottyM,而不是新的数据类型a读者的建议。目前我并不担心runDB
函数,它是比较容易的部分。我所需要的只是解决读者的问题并将池传递给处理程序。到目前为止,一切都不起作用。简单模拟“全局“数周来,变量变成了我的一个全球性问题……哦。。好。。。我想我在你的帖子上打了评论后就得到了。我应该从Web.Scotty.Trans
中使用get
函数,而不是从Web.Scotty
中。我用工作版本更新了问题。我仍然不太理解scottyT
参数,以及为什么我们应用runReaderT
两次?如果你能解释一下,我会非常感激的。再次感谢您。您必须为Scotty和ActionT分别执行一次操作-理论上,我认为您可以为两者注入不同的环境。请注意,这在Scotty 0.10中发生了变化。您现在只使用一个runReaderT
,即在
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Encoding as T
import Data.Text.Lazy (Text)
import Control.Monad.Reader
import Web.Scotty.Trans
import Data.Pool
import Database.MongoDB
type ScottyD = ScottyT Text (ReaderT (Pool Pipe) IO)
type ActionD = ActionT Text (ReaderT (Pool Pipe) IO)
-- Get data from config
ip = "127.0.0.1"
db = "basal"
main = do
pool <- createPool (runIOE $ connect $ host ip) close 1 300 5
let read = \r -> runReaderT r pool
scottyT 3000 read read basal
-- Application, meaddleware and routes
basal :: ScottyD ()
basal = do
get "/" shoot
-- Route action handlers
shoot :: ActionD ()
shoot = do
r <- rundb $ fetch $ select [] "computers"
html $ T.pack $ show r
-- Database access shortcut
rundb :: Action IO a -> ActionD (Either Failure a)
rundb a = do
pool <- lift ask
liftIO $ withResource pool (\pipe -> access pipe master db a)
run :: Pool Pipe -> Action IO a -> IO (Either Failure a)
run pool act =
flip withResource (\x -> access x master db act) =<< pool
run :: Action IO a -> ReaderT (Pool Pipe) IO (Either Failure a)
run act = do
pool <- ask
withResource pool (\x -> access x master db act)
-- remember: run act :: ReaderT (Pool Pipe) IO (Either Failure a)
runReaderT (run act) pool
basal :: ScottyT Text (ReaderT (Pool Pipe) IO) ()
basal = do
middleware $ staticPolicy (...)
get "/json" showJson
showJson :: ActionT Text (ReaderT (Pool Pipe) IO) ()
showJson = do
pool <- lift ask
let run act = withResource pool (\p -> access p master "index act)
d <- liftIO $ run $ fetch $ select [] "tables"
text . TL.pack $ either (const "") show d
type MCScottyM = ScottyT TL.Text (ReaderT (Pool Pipe) IO)
type MCActionM = ActionT TL.Text (ReaderT (Pool Pipe) IO)
main :: IO ()
main = do
pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5
scottyT 3000 (f pool) (f pool) $ app
where
f = \p -> \r -> runReaderT r p
app :: MCScottyM ()
app = do
middleware $ staticPolicy (noDots >-> addBase "public")
get "/" $ do
p <- runDB dataSources
html $ TL.pack $ show p
runDB :: Action IO a -> MCActionM (Either Failure a)
runDB a = (lift ask) >>= (\p -> liftIO $ withResource p (\pipe -> access pipe master "botland" a))
dataSources :: Action IO [Document]
dataSources = rest =<< find (select [] "datasources")
runDB :: Action IO a -> MCActionM (Either Failure a)
runDB a = do
p <- lift ask
liftIO $ withResource p db
where
db pipe = access pipe master "botland" a