我是 Haskell 的新手,所以我很难理解 Yesod 中使用的所有高级功能,例如 type instances和 equality constraints .我正在尝试实现 bracket pattern在 Yesod 的测试框架中以获得设置/拆卸功能。这是我到目前为止所得到的(通过编辑更新):
module FishMother where
import Control.Exception.Lifted
import TestImport
import Database.Persist
import Database.Persist.GenericSql
import Model
insertYellowfinTuna :: OneSpec Connection FishId
insertYellowfinTuna = runDB . insert $ Fish "Yellowfin Tuna"
deleteFish :: FishId -> OneSpec Connection ()
deleteFish = runDB . delete
withYellowfinTuna :: FishId -> OneSpec Connection ()
withYellowfinTuna = bracket insertYellowfinTuna deleteFish
编译错误如下:
tests/FishMother.hs:18:21:
Couldn't match type `FishId
-> Control.Monad.Trans.State.Lazy.StateT
(Yesod.Test.OneSpecData Connection) IO ()'
with `Key SqlPersist Fish'
Expected type: FishId -> OneSpec Connection ()
Actual type: (FishId
-> Control.Monad.Trans.State.Lazy.StateT
(Yesod.Test.OneSpecData Connection) IO ())
-> Control.Monad.Trans.State.Lazy.StateT
(Yesod.Test.OneSpecData Connection) IO ()
In the return type of a call of `bracket'
In the expression: bracket insertYellowfinTuna deleteFish
In an equation for `withYellowfinTuna':
withYellowfinTuna = bracket insertYellowfinTuna deleteFish
我做错了什么?
最佳答案
重读问题后,我认为简单的答案是使用 a lifted bracket ,它将处理所有变压器问题。我也会留下我原来的答案,因为它可能会让您更深入地了解正在发生的事情。
本例中的问题是 liftIO
的使用。让我们看看类型签名:
liftIO :: MonadIO m => IO a -> m a
这意味着您可以执行任意 I/O 操作(例如,从文件中读取)并在任何允许执行 I/O 的 monad 中运行它,例如数据库 monad。但是,您尝试做的实际上是相反的:将数据库 monad 操作作为正常的 I/O 操作来运行。这不能直接完成,因为数据库操作依赖于额外的上下文 - 特别是数据库连接 - IO
monad 不提供。
那么如何将数据库操作转换为 IO 操作呢?我们必须展开。展开是 monad 转换器 的常见事件,它可以被认为是层层叠加。提升(如在 liftIO
中)允许您从较低层执行操作并将其移动到较高层。展开一层。由于展开方式取决于特定的转换器,因此每个转换器都有自己的展开函数。
在 Persistent 的情况下,您可以使用 withSqliteConn 或等效的后端,请参阅 Persistent chapter 中的概要了解更多详情。
关于testing - Yesod/Persistent testing with bracket 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14393117/