2012-11-11 35 views
11

首先,我想要完成的任務的簡化版本:我有幾個大文件(總計30GB),我希望修剪重複條目。爲此,我建立了一個數據散列數據庫,並逐個打開這些文件,散列每個項目,並將其記錄在數據庫和輸出文件中(如果散列不在數據庫中)。從Conduit中使用持久性

我知道如何用iteratees和枚舉器來做到這一點,我想試試管道。我也知道如何用管道做到這一點,但現在我想用管道&執行。我遇到了各種類型的問題,可能還有ResourceT的整個概念。

下面是一些僞代碼來說明這個問題:

withSqlConn "foo.db" $ runSqlConn $ runResourceT $ 
    sourceFile "in" $= parseBytes $= dbAction $= serialize $$ sinkFile "out" 

問題在於dbAction功能。自然,我想在這裏訪問數據庫。由於它的作用基本上是一個過濾器,我首先想到把它寫這樣的:

dbAction = CL.mapMaybeM p 
    where p :: (MonadIO m, MonadBaseControl IO (SqlPersist m)) => DataType -> m (Maybe DataType) 
      p = lift $ putStrLn "foo" -- fine 
      insert $ undefined -- type error! 
      return undefined 

特定的錯誤我得到的是:

Could not deduce (m ~ b0 m0) 
from the context (MonadIO m, MonadBaseControl IO (SqlPersist m)) 
    bound by the type signature for 
      p :: (MonadIO m, MonadBaseControl IO (SqlPersist m)) => 
          DataType -> m (Maybe DataType) 
    at tools/clean-wac.hs:(33,1)-(34,34) 
    `m' is a rigid type variable bound by 
     the type signature for 
     p :: (MonadIO m, MonadBaseControl IO (SqlPersist m)) => 
         DataType -> m (Maybe (DataType)) 
     at tools/clean-wac.hs:33:1 
Expected type: m (Key b0 val0) 
    Actual type: b0 m0 (Key b0 val0) 

注意,這可能是由於錯誤的假設我在設計類型簽名。如果我註釋掉類型簽名,也拆除lift說法,錯誤消息變成:

No instance for (PersistStore ResourceT (SqlPersist IO)) 
    arising from a use of `p' 
Possible fix: 
    add an instance declaration for 
    (PersistStore ResourceT (SqlPersist IO)) 
In the first argument of `CL.mapMaybeM', namely `p' 

因此,這意味着我們無法通過ResourceT訪問PersistStore呢?

我不能寫我自己的管道要麼不使用CL.mapMaybeM

dbAction = filterP 
filterP :: (MonadIO m, MonadBaseControl IO (SqlPersist m)) => Conduit DataType m DataType 
filterP = loop 
    where loop = awaitE >>= either return go 
      go s = do lift $ insert $ undefined -- again, type error 
        loop 

這導致了另一種類型的錯誤,我不完全理解。

Could not deduce (m ~ b0 m0) 
from the context (MonadIO m, MonadBaseControl IO (SqlPersist m)) 
    bound by the type signature for 
      filterP :: (MonadIO m, 
           MonadBaseControl IO (SqlPersist m)) => 
           Conduit DataType m DataType 
    `m' is a rigid type variable bound by 
     the type signature for 
     filterP :: (MonadIO m, 
          MonadBaseControl IO (SqlPersist m)) => 
          Conduit DataType m DataType 
Expected type: Conduit DataType m DataType 
    Actual type: Pipe 
       DataType DataType DataType() (b0 m0)() 
In the expression: loop 
In an equation for `filterP' 

所以,我的問題是:是否有可能使用持久性,就像我打算在導管裏一樣?如果,如何?我知道,因爲我可以在管道內使用liftIO,所以我可以去使用​​,但我想明確地使用persistent,以瞭解它是如何工作的,並且因爲我喜歡它的db-backend不可知論。

+0

您是否嘗試過使用'lift',而不是'liftIO'? –

+0

啊,是的,當然'liftIO'對整個'do'塊施加一個約束。但這隻能解釋爲什麼第一個錯誤信息與第二個錯誤信息不同。我會在一秒內更新這篇文章,以反映如果您刪除liftIO聲明會發生什麼情況。 –

+0

順便說一句,即使'lift'已經對monad類型施加了'IO'限制。我注意到你必須*刪除* lift'語句以達到該錯誤信息。如果你不這樣做(但是保留'lift $ print「」'in),你會得到'不能與實際類型'IO()'匹配的預期類型'SqlPersist m0 a0'。 –

回答

7

下面的代碼適合我。在這段時間內,框架是否有可能發展而現在的情況正在發揮作用?

但是請注意,由於世界發生了一些變化,或者我沒有全部代碼,所以必須進行以下更改。我在GHC 7.6.3中使用了conduit-1.0.9.3和persistent-1.3.0。

  • parseBytesserialise因爲我沒有你的定義和界定DataType = ByteString來代替。

  • 引入了Proxy參數和undefined值的顯式類型簽名,以避免類型族注入問題。這些可能不會出現在您的真實代碼中,因爲它將具有val的具體或外部確定類型。

  • 使用await而不是awaitE,只是用來作爲()類型替代的Left情況下,作爲awaitE已經退役。

  • 通過了一個虛擬Connection創建功能withSqlConn - 也許我應該使用一些Sqlite特定功能?

下面的代碼:

{-# LANGUAGE FlexibleContexts, NoMonomorphismRestriction, 
      TypeFamilies, ScopedTypeVariables #-} 

module So133331988 where 

import Control.Monad.Trans 
import Database.Persist.Sql 
import Data.ByteString 
import Data.Conduit 
import Data.Conduit.Binary 
import Data.Proxy 

test proxy = 
    withSqlConn (return (undefined "foo.db")) $ runSqlConn $ runResourceT $ 
     sourceFile "in" $= dbAction proxy $$ sinkFile "out" 

dbAction = filterP 

type DataType = ByteString 

filterP 
    :: forall m val 
    . (MonadIO m, MonadBaseControl IO (SqlPersist m) 
     , PersistStore m, PersistEntity val 
     , PersistEntityBackend val ~ PersistMonadBackend m) 
    => Proxy val 
    -> Conduit DataType m DataType 
filterP Proxy = loop 
    where loop = await >>= maybe (return()) go 
      go s = do lift $ insert (undefined :: val) 
        loop 
+0

很久以前我問過這個,所以我幾乎不記得這是怎麼回事。但我認爲這應該清除它。是的,我認爲自從我問這個問題以來,相關的API就發生了很大的變化。謝謝! –

+0

我真的有點失望,因爲我只是希望有一個多汁的類型系統問題想辦法:-) –