Сначала, упрощенная версия задачи, которую я хочу выполнить: у меня есть несколько больших файлов (в размере 30 ГБ), которые я хочу обрезать для повторяющихся записей. С этой целью я устанавливаю базу данных хэшей данных и открываю файлы один за другим, хэшируя каждый элемент и записывая его в базу данных и выходной файл, если его хэш еще не был в базе данных.
Я знаю, как это сделать с помощью итераций, счетчиков, и я хотел попробовать каналы. Я также знаю, как это сделать с помощью кабелепроводов, но теперь я хочу использовать кабелепроводы и настойчивые. У меня проблемы с типами и, возможно, со всей концепцией 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'
Итак, это означает, что мы вообще не можем получить доступ к PersistStore
через ResourceT
?
Я тоже не могу написать свой собственный канал, не используя 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
внутри кабелепровода, я мог бы просто пойти и использовать, скажем HDBC
, но я хотел использовать постоянное явно, чтобы понять, как он работает, и потому что мне нравится его db-backend агностицизм.