2012-11-11 9 views
11

Zuerst, eine vereinfachte Version der Aufgabe, die ich erreichen möchte: Ich habe mehrere große Dateien (in Höhe von 30 GB), die ich für doppelte Einträge löschen möchte. Zu diesem Zweck errichte ich eine Datenbank mit Hashes der Daten und öffne die Dateien einzeln, hashe jedes Element und zeichne es in der Datenbank und der Ausgabedatei auf, wenn sein Hash nicht bereits in der Datenbank vorhanden ist.Verwendung von persistent aus einem Conduit

Ich weiß, wie dies mit iterates, Enumeratoren zu tun, und ich wollte Conduits versuchen. Ich weiß auch, wie man es mit Conduits macht, aber jetzt möchte ich Conduits & persistent verwenden. Ich habe Probleme mit den Typen und möglicherweise mit dem gesamten Konzept von ResourceT.

Hier einig Pseudo-Code, um das Problem zu veranschaulichen:

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

Das Problem liegt in der dbAction Funktion. Ich möchte natürlich hier auf die Datenbank zugreifen. Da die Maßnahmen, die sie im Grunde tut, ist nur ein Filter, dachte ich zuerst, dass es so schreiben:

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 

Der spezifische Fehler ich erhalte, ist:

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) 

Beachten Sie, dass dies zu falschen Annahmen zurückzuführen sein könnte Ich habe die Typensignatur entworfen. Wenn ich die Art Unterschrift kommentieren und auch die lift Anweisung entfernen, wird die Fehlermeldung in:

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' 

So bedeutet dies, dass wir nicht die PersistStore überhaupt über ResourceT zugreifen können?

Ich kann meine eigene Conduit entweder nicht schreiben, ohne CL.mapMaybeM mit:

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 

Diese noch in einer anderen Art Fehler geführt Ich verstehe nicht ganz.

Also, meine Frage ist: ist es möglich zu verwenden hartnäckig wie ich in einem Conduit überhaupt wollte? Und wenn, wie? Ich bin mir bewusst, dass, da ich liftIO innerhalb des Conduit verwenden kann, ich einfach gehen und sagen könnte HDBC, aber ich wollte hartnäckig explizit verwenden, um zu verstehen, wie es funktioniert, und weil ich seine db-Backend-Agnostizismus mag.

+0

Haben Sie versucht, statt 'liftIO' mit' lift'? –

+0

Ah, ja, sicher "LiftIO" auferlegt dem gesamten Do-Block eine Einschränkung. Aber das erklärt nur, warum die erste Fehlermeldung von der zweiten abweicht. Ich werde den Beitrag in einer Sekunde aktualisieren, um zu reflektieren, was passiert, wenn Sie die Anweisung von LiftIO entfernen. –

+0

BTW, sogar "Lift" auferlegt dem Monad-Typ bereits "IO" -Einschränkungen. Ich habe bemerkt, dass du die Anweisung 'lift' * entfernen musst, um diese Fehlermeldung zu erhalten. Wenn Sie dies nicht tun (aber 'lift $ print" "' in) beibehalten, erhalten Sie stattdessen 'Konnte nicht mit dem erwarteten Typ 'SqlPersist m0 a0' mit dem tatsächlichen Typ 'IO()' übereinstimmen. –

Antwort

7

Der Code unten kompiliert für mich gut. Ist es möglich, dass sich die Frameworks in der Zwischenzeit weiterentwickelt haben und die Dinge jetzt einfach funktionieren?

Beachten Sie jedoch die folgenden Änderungen, die ich vornehmen musste, da sich die Welt ein wenig verändert hat oder ich nicht den ganzen Code hatte. Ich benutzte conduit-1.0.9.3 und persistent-1.3.0 mit GHC 7.6.3.

  • Ausgelassene parseBytes und serialise da ich nicht Ihre Definitionen haben und definiert DataType = ByteString statt.

  • Einführung eines Proxy Parameters und einer expliziten Typsignatur für den undefined-Wert, um Probleme mit der Injektivität der Typfamilie zu vermeiden. Diese entstehen in Ihrem echten Code wahrscheinlich nicht, da sie einen konkreten oder extern bestimmten Typ für val haben.

  • Gebrauchte await statt awaitE und nur () als Typ verwendet für die Left Fall zu ersetzen, wie awaitE im Ruhestand ist.

  • Wurde ein Dummy übergeben Connection Creation-Funktion zu withSqlConn - vielleicht hätte ich einige Sqlite-spezifische Funktion verwendet werden sollen?

Hier ist der Code:

{-# 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

Ich habe das so lange gefragt, dass ich mich kaum erinnerte, worum es ging. Aber ich denke, das sollte klären. Ja, ich denke, dass die fraglichen APIs sich seit meiner Anfrage geändert haben. Vielen Dank! –

+0

Ich war eigentlich ein bisschen enttäuscht, als es gerade funktionierte, da ich auf ein saftiges Systemproblem hoffen musste :-) –