Как использовать постоянный в стеке монад?

Мне нравилось изучать Haskell, и я думаю, что достиг хороших успехов с помощью людей здесь и в #haskell. Мое обучение в основном все еще происходит на том этапе, когда я смотрю на примеры и пытаюсь абстрагироваться от применяемых там методов и применять их к своему собственному коду.

В настоящее время я начал изучать разработку стеков монад для различных приложений и хочу включить в свое приложение функциональность персистентного фреймворка.

Вот мой стек монад:

newtype App a = App { unApp :: StateT AppState (SqlPersistT (ResourceT (LoggingT IO))) a }
                deriving ( Applicative
                         , Functor
                         , Monad
                         , MonadIO
                         , MonadState AppState
                         )

AppState — это просто тип данных записи, содержащий одно значение Int в этом примере.

Моя основная функция выглядит так:

main = runApp "./test.sqlite" (AppState 69) runMigrate

где runApp должен развернуть все монады:

runApp :: Text -> AppState -> App a -> IO a
runApp t s a = 
  runStdoutLoggingT . runResourceT . withSqliteConn t . runSqlConn . flip evalStateT s . unApp

а runMigrate — это приложение для запуска в монаде App. В этом случае я стремился просто заставить его запустить миграцию:

runMigrate :: App ()
runMigrate = return $ liftPersist $ runMigration migrateAll

Компилятор указывает, что я не знаю, что делаю с жалобой:

Main.lhs:59:16:
    Couldn't match type ‘m0 ()’ with ‘()’
    Expected type: App ()
      Actual type: App (m0 ())
    In the expression: return $ liftPersist $ runMigration migrateAll
    In an equation for ‘runMigrate’:
        runMigrate = return $ liftPersist $ runMigration migrateAll

Вопросы:

  1. Как правильно это сделать?

  2. Что произойдет, если я введу в свой стек монад ReaderT? Учитывая, что SqlPersistT на самом деле является ReaderT, как я могу убедиться, что ask соответствует реальному ReaderT, а не SqlPersistT?


person helpwithhaskell    schedule 17.10.2014    source источник


Ответы (1)


На ваш первый вопрос: return не является правильной функцией --- смысл return в том, что return x не выполняет никакой работы вашей монады и просто возвращает значение. Я думаю, вы, вероятно, хотите:

runMigrate = App $ lift $ runMigration migrateAll

App поднимает ваше определение newtype в вашу монаду; lift поднимает PersistT в упаковку StateT.

(Между прочим, я рекомендую называть App . lift . runMigration чем-то вроде runMigrationApp, если вы собираетесь его часто использовать.)

person Jonathan Cast    schedule 17.10.2014