1

我对 Haskell 很陌生,所以我无法吸收 Yesod 中使用的所有高级特性,例如类型实例相等约束。我正在尝试在 Yesod 的测试框架中实现括号模式,以获得 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

我究竟做错了什么?

4

1 回答 1

3

重新阅读问题后,我认为简单的答案是使用提升括号,它将处理所有变压器问题。我也会留下我原来的答案,因为它可能会更深入地了解正在发生的事情。


这种情况下的问题是liftIO. 让我们看一下类型签名:

liftIO :: MonadIO m => IO a -> m a

这意味着您可以采取任意 I/O 操作(例如,从文件中读取)并在任何允许执行 I/O 的 monad 中运行它,例如数据库 monad。但是,您尝试做的实际上恰恰相反:将数据库 monad 操作作为普通 I/O 操作运行。这不能直接完成,因为数据库操作依赖于额外的上下文 - 特别是数据库连接 - IOmonad 不提供。

那么如何将一个数据库动作变成一个 IO 动作呢?我们必须打开包装。展开是monad 转换器的常见活动,可以将其视为相互叠加的层。提升(如liftIO)允许您从较低层采取行动并将其向上移动到较高层。展开会去掉一层。由于解包方式取决于特定的转换器,因此每个转换器都有自己的解包函数。

在 Persistent 的情况下,您可以使用 withSqliteConn 或等效的后端,请参阅Persistent 章节中的概要了解更多详细信息。

于 2013-01-18T08:53:40.150 回答