Scotty:连接池作为monad reader [英] Scotty: connection pool as monad reader
问题描述
我从来没有使用Reader,只是在实践中从未接触过。所以我不知道如何去做,但我读了它。
我需要在Scotty中实现一个简单的数据库连接池,以便每个操作都可以使用池。该池必须是全局的,并且可以通过所有操作功能进行访问。我读到了读取monad的方法。如果还有其他方法,请告诉我。
您能帮我解释一下如何正确使用Reader吗?
如果我看到自己的例子是如何完成的,我可能会学得更快。
{ - #LANGUAGE OverloadedStrings# - }
模块DB其中
导入Data.Pool
导入Database.MongoDB
- 从配置
ip =127.0.0.1
db =index
- 创建连接池
pool :: IO(Pool Pipe)
pool = createPool(runIOE $ connect $ host ip)close 1 300 5
- 使用连接池运行数据库操作
run :: Action IO a - > IO(或者失败a)
执行act = flip withResource(\ x - > access x master db act)=<<游泳池
所以上面很简单。我想在每个Scotty操作中使用'run'函数来访问数据库连接池。现在,问题是如何将它包装在Reader monad中以使其可以被所有函数访问?我明白,'pool'变量必须与所有Scotty动作函数一样'全球'。
谢谢。
UPDATE
我正在用完整的代码片段更新问题。我在哪里通过函数链中的'pool'变量。如果有人可以展示如何改变它来利用monad Reader。
我不明白该怎么做。
{ - #LANGUAGE OverloadedStrings# - }
模块Main其中
导入Network.HTTP.Types
导入Web.Scotty
将合格的Data.Text导入为T
导入合格的Data.Text。 Lazy as LT
import Data.Text.Lazy.Internal
import Data.Monoid(mconcat)
import Data.Aeson(object,(。=),encode)
import Network .Wai.Middleware.Static
导入Data.Pool
导入Database.MongoDB
导入Control.Monad.Trans(升降机,升降机)
main = do
- 创建可供所有操作函数访问的连接池
pool< - createPool(runIOE $ connect $ host127.0.0.1)close 1 300 5
scotty 3000(basal pool)
basal :: Pool Pipe - > ScottyM()
basal pool = do
middleware $ staticPolicy(noDots> - > addBasestatic)
get/ json(showJson pool)
showJson :: Pool Pipe - > ActionM()
showJson pool = do
let run = withResource pool(\pipe-> access pipe masterindexact)
d< - lift $ run $ fetch(select []tables)
let r = either(const [])id d
text $ LT.pack $ show r
谢谢。
更新2
我试图按照下面的建议方式去做,但它不起作用。
如果有人有任何想法,请。编译错误的列表太长了,我甚至不知道从哪里开始....
main = do
pool< - createPool(runIOE $ connect $ host127.0.0.1)close 1 300 5
scotty 3000 $ runReaderT basal pool
basal :: ScottyT LT.Text( ReaderT(Pool Pipe)IO)()
basal = do
中间件$ staticPolicy(noDots> - > addBasestatic)
get/ json$ showJson
showJson :: ActionT LT.Text(ReaderT(Pool Pipe)IO)()
showJson = do
p< - lift ask
let rdb a = withResource p(\管道 - >访问管道主索引a)
j< - liftIO $ rdb $ fetch(选择[]表)
文本$ LT.pack $ show j
UPDATE 3
em> cdk 这个问题也有帮助:如何添加Reader monad到Scotty的monad
这是编译的版本。
导入限定的Data.Text.Lazy为T
导入限定的数据.Text.Lazy.Encoding as T
import Data.Text.Lazy(Text)
import Control.Monad.Reader
import Web.Scotty.Trans
import Data.Pool
import Database.MongoDB
$ b类型ScottyD = ScottyT文本(ReaderT(Pool Pipe)IO)
类型ActionD = ActionT Text(ReaderT(Pool Pipe)IO)
- 从配置中获取数据
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
$ b $ - 应用程序,meaddleware和路由
basal :: ScottyD()
basal = do
get /拍摄
- 路线动作处理程序
shoot :: ActionD()
拍摄=做
r < - rundb $ fetch $ select []computers
html $ T.pack $ show r
- 数据库访问快捷方式
rundb :: Action IO a - > ActionD(或者失败a)
rundb a = do
pool< - lift ask
liftIO $ withResource pool(\ pipe-> access pipe master db a)
我一直在努力弄清楚这个确切的问题。感谢关于这个SO问题的提示,以及其他研究,我提出了以下适用于我的方法。你缺少的关键是使用 scottyT
毫无疑问,编写runDB有一种更漂亮的方式,但是我在Haskell中没有太多经验,所以如果你可以做得更好,请发布它。
type MCScottyM = ScottyT TL.Text (ReaderT(Pool Pipe)IO)
type MCActionM = ActionT TL.Text(ReaderT(Pool Pipe)IO)
$ b $ main :: IO()
main = do
pool< - createPool(runIOE $ connect $ host127.0.0.1)close 1 300 5
scottyT 3000(f pool)(f pool)$ app
where
f = \\ \\ p - > \r - > runReaderT rp
app :: MCScottyM()
app = do
middleware $ staticPolicy(noDots> - > addBasepublic)
get/ $ do
p< - runDB dataSources
html $ TL.pack $ show p
runDB :: Action IO a - > MCActionM(或者失败a)
runDB a =(lift ask)>> =(\ p - > liftIO $ withResource p(\ pipe-> access pipe masterbotlanda))
dataSources :: Action IO [Document]
dataSources = rest =<<找到(选择[]datasources)
更新
我猜这个更漂亮了。
runDB :: Action IO a - > MCActionM(或者失败a)
runDB a = do
p< - lift ask
liftIO $ withResource p db
where
db pipe = access pipe masterbotland a
There are trillions of monad tutorial including the reader and it seems all clear when you read about it. But when you actually need to write, it becomes a different matter.
I'v never used the Reader, just never got to it in practice. So I don't know how to go about it although I read about it.
I need to implement a simple database connection pool in Scotty so every action can use the pool. The pool must be "global" and accessible by all action functions. I read that the way to do it is the Reader monad. If there are any other ways please let me know.
Can you please help me and show how to do this with the Reader correctly? I'll probably learn faster if I see how it is done with my own examples.
{-# 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
So the above is simple. and I want to use the 'run' function in every Scotty action to access the database connection pool. Now, the question is how to wrap it in the Reader monad to make it accessible by all functions? I understand that the 'pool' variable must be 'like global' to all the Scotty action functions.
Thank you.
UPDATE
I am updating the question with the full code snippet. Where I pass the 'pool' variable down the function chain. If someone can show how to change it to utilize the monad Reader please. I don't understand how to do it.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Network.HTTP.Types
import Web.Scotty
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Data.Text.Lazy.Internal
import Data.Monoid (mconcat)
import Data.Aeson (object, (.=), encode)
import Network.Wai.Middleware.Static
import Data.Pool
import Database.MongoDB
import Control.Monad.Trans (liftIO,lift)
main = do
-- Create connection pool to be accessible by all action functions
pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5
scotty 3000 (basal pool)
basal :: Pool Pipe -> ScottyM ()
basal pool = do
middleware $ staticPolicy (noDots >-> addBase "static")
get "/json" (showJson pool)
showJson :: Pool Pipe -> ActionM ()
showJson pool = do
let run act = withResource pool (\pipe -> access pipe master "index" act)
d <- lift $ run $ fetch (select [] "tables")
let r = either (const []) id d
text $ LT.pack $ show r
Thanks.
UPDATE 2
I tried to do it the way it was suggested below but it does not work. If anyone has any ideas, please. The list of compile errors is so long that I don't even know where to begin ....
main = do
pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5
scotty 3000 $ runReaderT basal pool
basal :: ScottyT LT.Text (ReaderT (Pool Pipe) IO) ()
basal = do
middleware $ staticPolicy (noDots >-> addBase "static")
get "/json" $ showJson
showJson :: ActionT LT.Text (ReaderT (Pool Pipe) IO) ()
showJson = do
p <- lift ask
let rdb a = withResource p (\pipe -> access pipe master "index" a)
j <- liftIO $ rdb $ fetch (select [] "tables")
text $ LT.pack $ show j
UPDATE 3
Thanks to cdk for giving the idea and thanks to Ivan Meredith for giving the scottyT suggestion. This question also helped: How do I add the Reader monad to Scotty's monad This is the version that compiles. I hope it helps someone and saves some time.
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)
I've been trying to figure out this exact problem myself. Thanks to hints on this SO question, and other research I've come up with the following which works for me. The key bit you were missing was to use scottyT
No doubt there is a prettier way to write runDB but I don't have much experience in Haskell, so please post it if you can do better.
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")
Update
I guess this a bit more pretty.
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
这篇关于Scotty:连接池作为monad reader的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!