这个问题与我的other question有关关于 smallCheck
的 Test.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
的东西(这需要一些看起来更可怕的编译器标志:FlexibleContexts
和UndecidableInstances
至少)?第一个参数(
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) Series目前定义为
newtype Series m a = Series (ReaderT Depth (LogicT m) a)
这意味着 m
是 LogicT
的基础 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 ()
身份
在实践中,您可以通过编写自定义驱动程序函数来利用这一事实 交错一些 monadic 效果,例如在所述驱动程序中记录生成的值(或类似的值)。
它也可能对我不知道的其他事情有用。
关于testing - 为什么smallCheck的 `Series`类在构造函数中有两个类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16557610/