Scotty:连接池作为monad reader [英] Scotty: connection pool as monad reader

查看:126
本文介绍了Scotty:连接池作为monad reader的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有数万亿的monad教程,包括读者,当你阅读它时,似乎很清楚。但是,当你真的需要写作的时候,它就变成了另外一回事。

我从来没有使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆