此问题与other question关于smallCheck
的{{1}}课程有关。当我尝试以下面的自然方式定义类Test.SmallCheck.Series
的实例时(通过@tel对上述问题的回答建议),我得到了编译器错误:
Serial
事实证明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
中的m
)是什么?
谢谢!
答案 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)
表示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 ()
的实例,则不能使用预先存在的驱动程序函数,例如Identity
。
在实践中,您可以通过编写自定义驱动程序函数来利用这一事实 交错一些monadic效应,如在所述驱动程序内记录生成的值(或某些此类)。
对于我不了解的其他事情也可能有用。