How to
自动派生实例CoArbitrary
,您的数据类型应该有一个实例Generic
,它又可以通过一些很好的语言扩展自动导出:
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data Validation e a =
Error e
| Scss a
deriving (Eq, Show, Generic)
然而,你的程序中最严重的错误是你正在测试[]
但不是你自己的类型applicative [(Scss "b", Scss "a", Scss "c")]
。这是定义applicative
测试包,详细信息省略:
applicative :: forall m a b c.
( Applicative m
, Arbitrary a, CoArbitrary a, Arbitrary b, Arbitrary (m a)
, Arbitrary (m (b -> c)), Show (m (b -> c))
, Arbitrary (m (a -> b)), Show (m (a -> b))
, Show a, Show (m a)
, EqProp (m a), EqProp (m b), EqProp (m c)
) =>
m (a,b,c) -> TestBatch
applicative = const ( "applicative"
, [ ("identity" , property identityP)
, ("composition" , property compositionP)
, ("homomorphism", property homomorphismP)
, ("interchange" , property interchangeP)
, ("functor" , property functorP)
]
)
简而言之,给定四种类型m
, a
, b
and c
,这个函数将创建一堆属性m
-- 作为一个应用函子 -- 应该满足,稍后你可以用随机测试它们a
b
c
产生的值QuickCheck
. [(Scss "b", Scss "a", Scss "c")]
有类型[(Validation String, Validation String, Validation String)]
makes m ~ []
.
所以你应该提供一些类型的值Validation e (a, b, c)
,或者根本没有值:您可能已经注意到const
就在定义中applicative
,只有参数的类型很重要:
main :: IO ()
main = quickBatch $ applicative (undefined :: Validation String (Int, Double, Char))
之后,您可以运行测试并获得格式正确的结果。但是不,您不应该以这种方式测试应用程序。
为什么不应该
测试由提供checkers
还远远不够。根据 GHC 的运行时单态性质及其处理歧义的方式,您必须提供四种具体的非多态类型来运行测试,例如Validation String (Int, Double, Char)
,测试模块将仅生成并测试这四种类型,而您的应用函子应该适用于满足上下文的任何类型。
IMO 大多数多态函数都不太适合单元测试框架:它无法针对所有可能的类型进行测试,因此必须选择要么仅使用手动选择的类型进行一些测试,要么对通用类型进行测试足够(就像 Free monad,当你的代码需要任意 monad 时,但通常“足够通用”在其他上下文中没有得到很好的定义)。
您最好严格检查您的实现,并证明所有情况下都满足所有法律,无论是用纸笔还是用 agda 等证明引擎。这是一个关于Maybe
这可能有帮助:证明组合律可能适用
编辑:请阅读评论。我不完全理解,但这意味着Integer
是用于单元测试多态函数的“足够通用”的类型。我发现Bartosz Milewski 的这篇博客文章及其参考书目是掌握参数性和自由定理思想的良好资源。