使用 MonadIO 测试类型类:"No instance nor default method" 错误

Testing a typeclass with MonadIO: "No instance nor default method" error

我有一个执行某些 IO 的类型类。我使用 MonadIO:

对其进行了一些概括
class MonadIO m => MonadDB m where
    getSomething :: String -> m Something
    getSomething s = -- do some IO stuff with liftIO

在测试中我希望替换实现以便我可以测试使用 getSomething 的函数,所以我这样做:

newtype WorkingDBM a = WorkingDBM (Identity a)
    deriving (Functor, Applicative, Monad)

instance MonadDB WorkingDBM where
    getSomething s = return $ Something "blah"

如果没有实例声明,代码会警告:

• No explicit implementation for ‘liftIO’
• In the instance declaration for ‘MonadIO WorkingDBM’

所以我补充:

instance MonadIO WorkingDBM

当然可以编译。

运行 Hspec 中的测试导致此运行时错误:

uncaught exception: NoMethodError (test/BlahSpec.hs:45:10-33: No instance nor default method for class operation liftIO

我尝试使用 Control.Monad.IO.Class 中的 liftIO

-- C is the qualified import for Control.Monad.IO.Class
liftIO = C.liftIO

但这会导致 NonTermination 运行时异常:

uncaught exception: NonTermination (<<loop>>)

有什么办法可以解决这个问题吗?

一种解决方案是在 WorkingDBM 中支持真实的 IO。例如:

newtype WorkingDBM a = WorkingDBM (IO a) -- N.B. IO not Identity
    deriving (Functor, Applicative, Monad)

instance MonadIO WorkingDBM where
    liftIO = WorkingDBM

MonadIO 的派生实例也可以正常工作;但是空实例不会,因为它等同于

instance MonadIO WorkingDBM where
    liftIO = undefined

这显然会在您第一次尝试实际操作时爆炸 IO