这个问题与我的other question有关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]))


我的问题是:


Identity放在那儿是“正确的事情”吗?我受到Test.Series.list函数类型的启发(当我第一次看到它时,我也发现它非常奇怪):

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


正确的做法是什么?如果我每次看到Identity时都盲目地放入它,我会好吗?我是否应该放一些类似Serial m Integer => Serial m Person的东西(这需要一些看上去更吓人的编译器标志:至少FlexibleContextsUndecidableInstances)?
第一个参数(m中的Serial m n)是什么?

谢谢!

最佳答案

我只是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之类的预先存在的驱动程序功能。

实际上,您可以通过编写一个自定义驱动程序函数来利用这一事实,
交错一些单调效应,例如在所述驱动程序内部记录生成的值(或类似值)。

对于我不知道的其他事情,它也可能有用。

07-25 21:21