我一直很喜欢学习Haskell,我觉得我在这里和#haskell的帮助下取得了一些进步。我的学习大部分时间仍然是我查看示例并尝试抽象出应用的技术并将其应用于我自己的代码。
目前,我已经开始考虑为各种应用程序开发monad堆栈,我希望将持久性框架的功能合并到我的应用程序中。
这是我的monad堆栈:
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
应该解开所有monad:
runApp :: Text -> AppState -> App a -> IO a
runApp t s a =
runStdoutLoggingT . runResourceT . withSqliteConn t . runSqlConn . flip evalStateT s . unApp
和runMigrate
是在App
monad中运行的应用程序。在这种情况下,我正在拍摄,只是为了让它进行迁移:
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
问题:
这样做的正确方法是什么?
如果在我的monad堆栈中我引入ReaderT
会怎么样?鉴于SqlPersistT
确实是ReaderT
,我如何确保ask
与真实ReaderT
匹配而不是SqlPersistT
?
答案 0 :(得分:1)
关于你的第一个问题:return
不是正确的函数--- return
的要点是return x
没有完成monad的工作而只返回一个值。我想你可能想要:
runMigrate = App $ lift $ runMigration migrateAll
App
将您的newtype
定义提升到您的monad中; lift
将PersistT
提升到包裹StateT
。
(顺便提一下,如果您要使用App . lift . runMigration
,我建议将runMigrationApp
命名为{{1}}。)