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.
Czy próbowałeś za pomocą 'lift' zamiast' liftIO'? –
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. –
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()''. –