我对Haskell很新,所以我无法吸收Yesod中使用的所有高级功能,例如type instances和equality constraints。我试图在Yesod的测试框架中实现bracket pattern以获得setUp / tearDown功能。这是我到目前为止所得到的(通过编辑更新):
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
我做错了什么?
答案 0 :(得分:3)
在重新阅读问题后,我认为简单的答案是使用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中的概要。