r.s*_*cky 19 haskell haskell-wai scotty
有数以万亿计的monad教程,包括读者,当你阅读它时似乎都很清楚.但是当你真正需要写作时,它就变成了另一回事.
我从未使用过读者,只是在实践中从未使用过.所以尽管我读到了它,但我不知道如何去做.
我需要在Scotty中实现一个简单的数据库连接池,以便每个操作都可以使用该池.该池必须是"全局的",并且可由所有操作功能访问.我读到了这样做的方法是Reader monad.如果还有其他方式,请告诉我.
你能帮我一下,并正确地说明如何用Reader做这个吗?如果我看到自己的例子如何完成,我可能会学得更快.
{-# 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
Run Code Online (Sandbox Code Playgroud)
所以上面很简单.我想在每个Scotty动作中使用'run'函数来访问数据库连接池.现在,问题是如何将它包装在Reader monad中以使其可被所有函数访问?我知道'pool'变量必须与所有Scotty动作函数"像全局"一样.
谢谢.
UPDATE
我正在使用完整的代码段更新问题.我在函数链中传递'pool'变量的地方.如果有人可以显示如何更改它以使用monad Reader请.我不明白该怎么做.
{-# 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
Run Code Online (Sandbox Code Playgroud)
谢谢.
更新2
我尝试按照下面建议的方式进行,但它不起作用.如果有人有任何想法,请.编译错误列表很长,我甚至不知道从哪里开始....
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
Run Code Online (Sandbox Code Playgroud)
更新3
感谢cdk提出这个想法并感谢Ivan Meredith给出了scottyT的建议.这个问题也有帮助:如何将Reader monad添加到Scotty的monad 这是编译的版本.我希望它可以帮助某人并节省一些时间.
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 Code Online (Sandbox Code Playgroud)
我一直试图弄清楚这个确切的问题.感谢有关此SO问题的提示,以及其他研究,我提出了以下哪些对我有用.你缺少的关键是使用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")
Run Code Online (Sandbox Code Playgroud)
我想这更漂亮一点.
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
Run Code Online (Sandbox Code Playgroud)