Сочетание Persistent с журналированием RIO для создания дампа таблицы
Я пишу игрушечный пример, чтобы изучить доступ к базе данных Haskell с помощью библиотеки Persistent. Чтобы поиграть, я хочу посмотреть, что находится в БД (SQLite в памяти):
import qualified Database.Persist.Sql as PSQL
import qualified Data.Conduit.List as CL
import Data.Conduit ( ($$) )
import Control.Monad.IO.Class (liftIO)
dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery "select * from " <> tableName [] $$ CL.mapM_ (liftIO . print)
(Взято из школы Haskell)
Поскольку я хочу использовать библиотеки RIO для своих приложений, вышеуказанное не работает: мне нужно использовать одну из функций регистрации RIO вместо print, и эта функция должна выполняться в монаде RIO. Вот моя попытка сделать это:
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
[..]
import RIO
import qualified Database.Persist.Sql as PSQL
import Data.Conduit ( ($$) )
import qualified Data.Conduit.List as CL
dumpTable :: (HasLogFunc env) => Text -> RIO env ()
dumpTable tableName =
let query = "select * from " <> tableName
in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
Однако этот код не выполняет проверку типа. Я получаю следующую ошибку:
• Could not deduce (PSQL.BackendCompatible PSQL.SqlBackend env)
arising from a use of ‘PSQL.rawQuery’
from the context: HasLogFunc env
bound by the type signature for:
dumpTable :: forall env. HasLogFunc env => Text -> RIO env ()
at src/Persistence/DbInspect.hs:13:1-51
• In the first argument of ‘($$)’, namely ‘PSQL.rawQuery query []’
In the expression:
PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
In the expression:
let query = "select * from " <> tableName
in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
|
16 | in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
| ^^^^^^^^^^^^^^^^^^^^^^
Я не понимаю, что означает эта ошибка. Было бы здорово, если бы кто-нибудь мог дать мне несколько советов о том, как продолжить и проанализировать эту ошибку, тем самым улучшив мое понимание задействованных классов типов и монад.
1 ответ
Во-первых, вместо
dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery "select * from <> tableName" [] $$ CL.mapM_ (liftIO . print)
ты наверное хочешь этого
dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery ("select * from " <> tableName) [] $$ CL.mapM_ (liftIO . print)
Теперь, предполагая эту версию, вы выбрали конкретный тип. IO
за dumpTable
который не должен вводить проверку.
Функция должна быть написана так
dumpTable
:: (MonadResource m, MonadReader env m,
BackendCompatible SqlBackend env) =>
Text -> m ()
dumpTable tableName = PSQL.rawQuery ("select * from " <> tableName) [] $$ CL.mapM_ (liftIO . print)
Я не знаю, какой конкретный пример вы имеете в виду, но простой пример для runQuery
будет выглядеть примерно так
main :: IO ()
main = runSqlite ":memory:" $ do
buildDb
dumpTable
buildDb
:: ReaderT SqlBackend (NoLoggingT (ResourceT IO)) (Key Tutorial)
buildDb = do
runMigrationSilent migrateTables
insert $ Tutorial "Basic Haskell" "https://fpcomplete.com/school/basic-haskell-1" True
insert $ Tutorial "A monad tutorial" "https://fpcomplete.com/user/anne/monads" False
insert $ Tutorial "Yesod usage" "https://fpcomplete.com/school/basic-yesod" True
insert $ Tutorial "Putting the FUN in functors" "https://fpcomplete.com/user/anne/functors" False
insert $ Tutorial "Basic Haskell" "https://fpcomplete/user/anne/basics" False
dumpTable
:: ReaderT SqlBackend (NoLoggingT (ResourceT IO)) ()
dumpTable = rawQuery "select * from Tutorial" [] $$ CL.mapM_ (liftIO . print)
Приведенный выше пример взят из дампа таблицы
Не вдаваясь в подробности, способ удовлетворить эти ограничения ReaderT SqlBackend (NoLoggingT (ResourceT IO))
заключается в использовании преобразователей каждой монады ' run
функции. ЗаReaderT
это было бы runReaderT
т.е. runReaderT configData $ monadReaderConstraiendFunction
.
В любом случае, вы можете посмотреть, как Monad Transformers
поработайте, прежде чем погрузиться в эту библиотеку. Это не займет много времени, и как только вы поймете суть этого, вы сможете отладить любые дальнейшие проблемы.
Кстати, давайте взглянем на эту часть сообщения об ошибке.
• Could not deduce (PSQL.BackendCompatible PSQL.SqlBackend env)
arising from a use of ‘PSQL.rawQuery’
from the context: HasLogFunc env
и ваш тип функции
dumpTable :: (HasLogFunc env) => Text -> RIO env ()
Монада env
ограничен HasLogFunc
но функция rawQuery
требуется несколько других контекстов для работы, как мы видели выше.
Вы можете видеть это из rawQuery
тип функции (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env)
.
По сути, нам нужно помочь GHC, явно указав их в сигнатуре типа. Я не знаю, как вы взаимодействуете сRIO
монада, но в самом общем случае должен выглядеть так
dumpTable :: (HasLogFunc env
, PSQL.BackendCompatible PSQL.SqlBackend env
, MonadResource (RIO env)
)
=> Text
-> RIO env ()
dumpTable tableName =
let query = "select * from " <> tableName
in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
Теперь это будет проверка типа.