簡體   English   中英

Haskell IO流和Groundhog數據庫使用情況

[英]Haskell IO-Streams and Groundhog db usage

如何編譯以下程序? 我以某種方式無法逃脫錯誤"No instance for (PersistBackend IO)

我的目的是看到如何使用io-streams有效填充數據庫表。 makeOutputStream的類型為(Maybe a -> IO ()) -> IO (OutputStream a) makeOutputStream (Maybe a -> IO ()) -> IO (OutputStream a)insertWords返回m () ,並且不接受IO ()作為返回類型。

(最后補充:找到了解決方法,但這不是問題的答案。請參見下文。)

錯誤消息為:

Words_read2.hs:30:36:
    No instance for (PersistBackend IO)
      arising from a use of `insertWord'
    Possible fix: add an instance declaration for (PersistBackend IO)
    In the first argument of `Streams.makeOutputStream', namely
      `insertWord'
    In a stmt of a 'do' block:
      os <- Streams.makeOutputStream insertWord
    In the expression:
      do { is <- Streams.handleToInputStream h >>= Streams.words;
           os <- Streams.makeOutputStream insertWord;
           Streams.connect is os }

產生此錯誤的代碼是:

{-# LANGUAGE GADTs, TypeFamilies, TemplateHaskell, QuasiQuotes, FlexibleInstances, FlexibleContexts, StandaloneDeriving #-}

import qualified Data.ByteString as B
import           Data.Maybe
import           Control.Monad.IO.Class (MonadIO, liftIO)
import           Database.Groundhog.Core 
import           Database.Groundhog.TH
import           Database.Groundhog.Sqlite
import           System.IO
import           System.IO.Streams.File
import qualified System.IO.Streams as Streams

data Words = Words {word :: String} deriving (Eq, Show)

mkPersist defaultCodegenConfig [groundhog|
definitions:
  - entity: Words
|]

insertWord :: (MonadIO m, PersistBackend m) => Maybe B.ByteString -> m ()
insertWord wo = case wo of
        Just ww ->  insert_ $ Words ((show . B.unpack) ww)
        Nothing -> return ()

main = do
  withSqliteConn "words2.sqlite" $ runDbConn $ do
     runMigration defaultMigrationLogger $ migrate (undefined :: Words)
     liftIO $ withFile "web2" ReadMode $ \h -> do  -- a link to /usr/share/dict/web2 - a  list of words one per line 
        is <- Streams.handleToInputStream h >>= Streams.words 
        os <- Streams.makeOutputStream insertWord
        Streams.connect is os

作為一種變通方法,我們可以用其他方式做一些事情:我們不嘗試在runDbConn內部runDbConn ,而是將句柄返回到(連接池)連接並將其傳遞。 這個想法來自於對問題的解答: 制作PersistBackend的自定義實例

{-# LANGUAGE GADTs, TypeFamilies, TemplateHaskell, QuasiQuotes, FlexibleInstances, FlexibleContexts, StandaloneDeriving #-}

import qualified Data.ByteString as B
import           Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import           Control.Monad.IO.Class -- (MonadIO, liftIO)
import           Control.Monad.Trans.Control
import           Database.Groundhog.Core 
import           Database.Groundhog.TH
import           Database.Groundhog.Sqlite
import           System.IO
import           System.IO.Streams.File
import qualified System.IO.Streams as Streams

data Words = Words {word :: T.Text} deriving (Eq, Show)

mkPersist defaultCodegenConfig [groundhog|
definitions:
  - entity: Words
|]

main = do
  gh <- do withSqlitePool "words5.sqlite" 5 $ \pconn -> return pconn 
  runDbConn (runMigration defaultMigrationLogger $ migrate (undefined :: Words)) gh
  withFile "web3" ReadMode $ \h -> do  -- 500 words from /usr/share/dict/web2 - a list of words one per line 
    is <- Streams.handleToInputStream h >>= Streams.words 
    os <- Streams.makeOutputStream (iw2db gh)
    Streams.connect is os

iw2db :: (MonadIO m, MonadBaseControl IO m, ConnectionManager cm Sqlite) => cm -> Maybe B.ByteString -> m()
iw2db gh (Just x) = runDbConn (insert_ $ Words (T.decodeUtf8 x)) gh
iw2db gh Nothing  = return ()

土撥鼠行動只能在Monad(PersistBackend實例)中運行。 無法將IO用作其實例,因為與DbPersist不同,它不攜帶連接信息。

我喜歡變通方法中的代碼,但是可以使其速度更快。 現在,每個操作都在由runDbConn打開的自己的事務中運行。 為了避免這種情況,我們可以從池中打開一個連接並開始一個事務。 然后,每個操作將重用此連接,從而避免事務開銷。 在這種情況下,createSqlitePool也比使用SqlitePool更好。

{-# LANGUAGE GADTs, TypeFamilies, TemplateHaskell, QuasiQuotes, FlexibleInstances, FlexibleContexts, StandaloneDeriving #-}

import qualified Data.ByteString as B
import           Data.Maybe    
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import           Control.Monad.IO.Class -- (MonadIO, liftIO)
import           Control.Monad.Trans.Control
import           Database.Groundhog.Core 
import           Database.Groundhog.TH
import           Database.Groundhog.Sqlite
import           System.IO
import           System.IO.Streams.File
import qualified System.IO.Streams as Streams
import Control.Monad.Logger (MonadLogger, NoLoggingT(..))
data Words = Words {word :: T.Text} deriving (Eq, Show)

mkPersist defaultCodegenConfig [groundhog|
definitions:
  - entity: Words
|]

main = do
  gh <- createSqlitePool "words5.sqlite" 5
  runDbConn (runMigration defaultMigrationLogger $ migrate (undefined :: Words)) gh
  withFile "/usr/share/dict/words" ReadMode $ \h -> do  -- 500 words from /usr/share/dict/web2 - a list of words one per line 
    is <- Streams.handleToInputStream h >>= Streams.words
    withConn (\conn -> liftIO $ do -- (conn :: Sqlite) with opened transaction
       os <- Streams.makeOutputStream (iw2db conn)
-- It is important to put Streams.connect inside withConn so that it uses the same transaction
-- If we put it outside, the transaction will be already closed and Sqlite will automatically do a new transaction for each insert
       Streams.connect is os) gh

iw2db :: (MonadIO m, MonadBaseControl IO m, ConnectionManager cm Sqlite)
      => cm -> Maybe B.ByteString -> m ()
iw2db gh (Just x) = runDbConnNoTransaction (insert_ $ Words (T.decodeUtf8 x)) gh
iw2db gh Nothing  = return ()

-- Probably this function should go to the Generic module
runDbConnNoTransaction :: (MonadBaseControl IO m, MonadIO m, ConnectionManager cm conn) => DbPersist conn (NoLoggingT m) a -> cm -> m a
runDbConnNoTransaction f cm = runNoLoggingT (withConnNoTransaction (runDbPersist f) cm)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM