2012-11-11 12 views
11

Najpierw up to wersja uproszczona zadania, które chcę wykonać: Mam kilka dużych plików (o pojemności do 30 GB), które chcę przycinać w poszukiwaniu duplikatów. W tym celu tworzę bazę danych z hasłami danych, i otwieram pliki jeden po drugim, mieszając każdy element i zapisując go w bazie danych, a plik wyjściowy, którego jego skrót nie był już w bazie danych.Użycie trwałego z wnętrza Conduit

Wiem, jak to zrobić z iteratami, modułami wyliczającymi i chciałem wypróbować kanały. Wiem też, jak to zrobić za pomocą przewodów, ale teraz chcę używać przewodów stałych. Mam problemy z typami i prawdopodobnie z całą koncepcją ResourceT.

Oto niektóre pseudo kod do zilustrowania problemu:

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

Problem leży w funkcji dbAction. Oczywiście chciałbym uzyskać dostęp do bazy danych. Ponieważ akcja robi to w zasadzie tylko filtr, to pierwsza myśl, aby napisać to tak:

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 

Specyficzny błąd pojawia się:

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) 

Należy pamiętać, że może to być spowodowane błędnych założeniach Zrobiłem w projektowaniu podpisu typu. Gdybym wykomentuj podpis typu, a także usunąć oświadczenie lift, komunikat o błędzie zamienia:

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' 

Więc to oznacza, że ​​nie może uzyskać dostępu do PersistStore w ogóle poprzez ResourceT?

nie mogę napisać własny Conduit albo bez użycia 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 

Spowodowało to kolejny błąd typu I nie w pełni zrozumieć.

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' 

Moje pytanie brzmi: czy możliwe jest korzystanie z uporczywego, tak jakbym zamierzał w ogóle w kabinie? A jeśli, jak? Jestem świadomy, że ponieważ mogę użyć liftIO wewnątrz kanału, mógłbym pójść i użyć, powiedzmy HDBC, ale chciałem użyć stałego uporczywie, aby zrozumieć jak to działa i ponieważ lubię jego agnostycyzm db-backend.

+0

Czy próbowałeś za pomocą 'lift' zamiast' liftIO'? –

+0

Ach, tak, na pewno 'liftIO' nakłada ograniczenie na cały blok' do'. Ale to tylko wyjaśnia, dlaczego pierwszy komunikat o błędzie różni się od drugiego. Zaktualizuję wpis w ciągu sekundy, aby odzwierciedlić, co się stanie, jeśli usuniesz instrukcję liftIO. –

+0

BTW, nawet 'lift' już nakłada ograniczenia' IO' na typ monady. Zauważyłem, że musisz * usunąć * polecenie 'lift', aby dotrzeć do tego komunikatu o błędzie. Jeśli tego nie zrobisz (ale zachowaj 'lift $ print" "' in), otrzymasz 'Nie można dopasować oczekiwanego typu 'SqlPersist m0 a0' z faktycznym typem 'IO()''. –

Odpowiedz

7

Poniższy kod jest odpowiedni dla mnie. Czy to możliwe, że ramy w międzyczasie poruszyły się i rzeczy po prostu działają?

Zwróć jednak uwagę na następujące zmiany, które musiałem wprowadzić, ponieważ świat nieco się zmienił lub nie miałem całego kodu. Użyłem kanału-1.0.9.3 i persistent-1.3.0 z GHC 7.6.3.

  • Pominięte parseBytes i serialise jak ja nie mają definicje i określone DataType = ByteString zamiast.

  • Wprowadzono parametr Proxy i jawny podpis typu dla wartości undefined, aby uniknąć problemów z wtryskiem rodziny. Prawdopodobnie nie powstają one w twoim prawdziwym kodzie, ponieważ będą miały konkretny lub zewnętrzny typ dla val.

  • Używane await zamiast awaitE i po prostu stosować () jako typ zastąpić przypadku Left, jak awaitE została wycofana.

  • Zdałem funkcję tworzenia obojętnego Connection na withSqlConn - może powinienem użyć jakiejś specyficznej funkcji Sqlite?

Oto kod:

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

Pytałem to już dawno temu, że prawie nie pamiętałem o co chodzi. Ale myślę, że to powinno to wyjaśnić. Tak, uważam, że omawiane API po prostu zmieniły się, odkąd zadałem to pytanie. Dzięki! –

+0

Byłem trochę rozczarowany, kiedy to zadziałało, ponieważ liczyłem na problem z soczystym systemem typów :-) –

Powiązane problemy