为什么 smallCheck 的"系列"类在构造函数中有两种类型?



这个问题与我关于smallCheckTest.SmallCheck.Series类的另一个问题有关。当我尝试以以下自然方式定义类Serial的实例时(由@tel对上述问题的回答建议),我得到编译器错误:

data Person = SnowWhite | Dwarf Int
instance Serial Person where ...

结果表明,Serial希望有两个参数。这反过来又需要一些编译器标志。以下作品:

{-# 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]))
我的问题是:
  1. 是否将Identity放在那里"正确的事情要做"?我受到Test.Series.list函数类型的启发(当我第一次看到它时,我也发现它非常奇怪):

    list :: Depth -> Series Identity a -> [a]
    

    正确的做法是什么?如果我只是盲目地把Identity在我看到它的时候?我应该把Serial m Integer => Serial m Person之类的东西代替(这需要一些更可怕的编译器标志:FlexibleContextsUndecidableInstances至少)?

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

    谢谢!

我只是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)

表示m是用于生成序列中的值的LogicT的基单子。例如,用IO代替m将允许在生成序列时发生IO操作。

在SmallCheck中,m也出现在Testable实例声明中,如instance (Serial m a, Show a, Testable m b) => Testable m (a->b)。如果您只有Identity的实例,则无法使用诸如smallCheck :: Testable IO a => Depth -> a -> IO ()之类的先前存在的驱动程序函数。

在实践中,您可以通过编写一个自定义驱动函数来利用这一事实在上述驱动程序中穿插一些单元效果,如记录生成的值(或其他类似的)。

最新更新