为什么smallCheck的`Series`类在构造函数中有两种类型?

时间:2013-05-15 05:36:26

标签: testing haskell automated-tests monads smallcheck

此问题与other question关于smallCheck的{​​{1}}课程有关。当我尝试以下面的自然方式定义类Test.SmallCheck.Series的实例时(通过@tel对上述问题的回答建议),我得到了编译器错误:

Serial

事实证明data Person = SnowWhite | Dwarf Int instance Serial Person where ... 想要有两个参数。反过来,这需要一些编译器标志。以下作品:

Serial

我的问题是:

  1. {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} import Test.SmallCheck import Test.SmallCheck.Series import Control.Monad.Identity data Person = SnowWhite | Dwarf Int instance Serial Identity Person where series = generate (\d -> SnowWhite : take (d-1) (map Dwarf [1..7])) 放在“正确的事情”中?我受到Identity函数类型的启发(当我第一次看到它时,我发现它非常奇怪):

    Test.Series.list

    正确的做法是什么?如果我只是在看到它的时候盲目地放入list :: Depth -> Series Identity a -> [a] ,我会没事吗?我是否应该添加类似Identity的内容(这需要一些更可怕的编译器标志:至少Serial m Integer => Serial m PersonFlexibleContexts)?

  2. 第一个参数(UndecidableInstances中的m)是什么?

    谢谢!

1 个答案:

答案 0 :(得分:4)

我只是smallcheck的用户,而不是开发人员,但我认为答案是

1)不是真的。你应该保持多态,你可以在没有上述扩展的情况下做到这一点:

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
import Test.SmallCheck
import Test.SmallCheck.Series
import Control.Monad.Identity

data Person = SnowWhite | Dwarf Int deriving (Show)

instance (Monad m) => Serial m Person where
        series = generate (\d -> SnowWhite : take (d-1) (map Dwarf [1..7]))

2)系列目前定义为

newtype Series m a = Series (ReaderT Depth (LogicT m) a)

表示mLogicT的基础monad,用于生成系列中的值。例如,编写IO代替m将允许在生成系列时执行IO操作。

在SmallCheck中,m实例声明中也会显示Testable,例如instance (Serial m a, Show a, Testable m b) => Testable m (a->b)。这具有以下具体效果:如果您只有smallCheck :: Testable IO a => Depth -> a -> IO ()的实例,则不能使用预先存在的驱动程序函数,例如Identity

在实践中,您可以通过编写自定义驱动程序函数来利用这一事实 交错一些monadic效应,如在所述驱动程序内记录生成的值(或某些此类)。

对于我不了解的其他事情也可能有用。