2013-10-30 59 views
2

如何編譯下面的程序?不知何故,我不能逃避錯誤"No instance for (PersistBackend IO)Haskell IO-Streams和Groundhog db的用法

我的目標是看到,如何高效地使用io-streams填充數據庫表。 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工作而返回的句柄連接(池),並通過它。這個想法來自SO回答問題: Making Custom Instances of 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() 
+0

,請複製粘貼你得到 – chamini2

+0

我很樂意幫助,但我不讀75行哈斯克爾的嘗試和挖掘出1個錯誤編譯錯誤,請降低該代碼 – jozefg

+0

好吧,我是能夠將代碼縮短到31行,我希望現在能夠更清楚地顯示問題出在哪裏。 – Gspia

回答

1

土撥鼠動作只能在monad中運行,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) 
+0

我正在測試groundhog-sqlite,hdbc-sqlite,direct-sqlite和hdbc-postgresql的性能,並想知道是否可以提高性能,因爲groundhog-sqlite和direct-sqlite比其他的速度慢得多。答案中的代碼實際上比使用HDBC-sqlite3的試驗更快,這確實很有趣,因爲上面的代碼有一個額外的「Words」構造函數。 – Gspia

+0

不知何故,這裏的persist-package並沒有立即編譯,而下面的工作gh < - 使用sqlitePool「words9.sqlite」5 $ \ pconn - > return pconn – Gspia