2017-01-16 38 views
0

У меня есть класс, который выполняет некоторые операции ввода-вывода. Я обобщил его немного с помощью MonadIO:Тестирование класса с помощью 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 

Я попытался с помощью liftIO из Control.Monad.IO.Class:

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

, но это приводит к исключению в NonTermination выполнения:

uncaught exception: NonTermination (<<loop>>) 

Любые идеи, как я могу решить t его, пожалуйста?

+0

Не выполняйте 'IO' в своем тесте или поддерживайте реальный' IO' в 'WorkDBM'. –

+0

Оказывается, это была 'liftIO' в функции, которую я тестировал, поэтому я не могу _not_ сделать IO. Что значит поддержка реальной? Я думал, что для этого достаточно «Монадио». –

+1

Я написал ответ с некоторыми подробностями о том, что я имею в виду. Но я оспорю ваше утверждение о том, что вы должны выполнить 'IO' внутри экземпляра« MonadDB »в своем тесте. –

ответ

1

Одним из решений является поддержка реального IO в WorkingDBM. Например:

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.

+0

Спасибо. Как я могу заставить использование «WorkDBM»? В настоящее время я использую 'unWorkingDBM (WorkDBM (Identity x)) = x' в своем тесте, который, очевидно, заставляет компилятор использовать мой« экземпляр teset »из« MonadDB ». Не имея эквивалента при использовании 'IO a' (из-за отсутствия конструктора данных для' IO' означает, что я не могу заставить компилятор использовать тестовый экземпляр (полученный или иначе ...) –

+0

@atc Вы все равно можете написать ' runWorkingDBM (WorkDBM x) = x', вам просто нужно дать ему тип 'runWorkingDBM :: WorkDBM a -> IO a', а не' runWorkingDBM :: WorkDBM a -> a'. –

+0

Благодарим за помощь, которая ее решает ! :) –