如何使用跳棋测试此应用实例? (没有 CoArbitrary 的实例(验证 e0 [Char]))
How do I test this applicative instance with checkers? (No instance for CoArbitrary (Validation e0 [Char]))
Checkers is a library for reusable QuickCheck properties, particularly for
standard type classes
如何编写跳棋实例来测试我的验证应用实例是否有效?
import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes
import Control.Applicative
import Data.Monoid
data Validation e a =
Error e
| Scss a
deriving (Eq, Show)
instance Functor (Validation e) where
fmap _ (Error e) = Error e
fmap f (Scss a) = Scss $ f a
instance Monoid e => Applicative (Validation e) where
pure = Scss
(<*>) (Scss f) (Scss a) = Scss $ f a
(<*>) (Error g) (Scss a) = Error g
(<*>) (Scss a) (Error g) = Error g
(<*>) (Error a) (Error g) = Error $ mappend a g
instance (Arbitrary a, Arbitrary b) => Arbitrary (Validation a b) where
arbitrary = do
a <- arbitrary
b <- arbitrary
elements [Scss a, Error b]
instance (Eq a, Eq b) => EqProp (Validation a b) where (=-=) = eq
main :: IO ()
main = quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
我想我快到了,但我收到如下错误:
chap17/Validation_applicative.hs:36:21: No instance for (CoArbitrary (Validation e0 [Char])) …
arising from a use of ‘applicative’
In the second argument of ‘($)’, namely
‘applicative [(Scss "b", Scss "a", Scss "c")]’
In the expression:
quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
In an equation for ‘main’:
main = quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
Compilation failed.
我试过像这样为验证添加 CoArbitrary 实例:
instance CoArbitrary (Validation a b)
但这会导致此错误消息:
chap17/Validation_applicative.hs:35:10: No instance for (GHC.Generics.Generic (Validation a b)) …
arising from a use of ‘Test.QuickCheck.Arbitrary.$gdmcoarbitrary’
In the expression: Test.QuickCheck.Arbitrary.$gdmcoarbitrary
In an equation for ‘coarbitrary’:
coarbitrary = Test.QuickCheck.Arbitrary.$gdmcoarbitrary
In the instance declaration for ‘CoArbitrary (Validation a b)’
chap17/Validation_applicative.hs:38:21: No instance for (Eq e0) arising from a use of ‘applicative’ …
The type variable ‘e0’ is ambiguous
Note: there are several potential instances:
instance Eq a => Eq (Const a b) -- Defined in ‘Control.Applicative’
instance Eq a => Eq (ZipList a) -- Defined in ‘Control.Applicative’
instance Eq a => Eq (Data.Complex.Complex a)
-- Defined in ‘Data.Complex’
...plus 65 others
In the second argument of ‘($)’, namely
‘applicative [(Scss "b", Scss "a", Scss "c")]’
In the expression:
quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
In an equation for ‘main’:
main = quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
Compilation failed.
如何
要自动派生 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
和 c
,此函数将创建一堆 m
- - 作为应用函子 -- 应该满足,稍后您可以使用 QuickCheck
生成的随机 a
b
c
值来测试它们。 [(Scss "b", Scss "a", Scss "c")]
具有类型 [(Validation String, Validation String, Validation String)]
使得 m ~ []
.
所以你应该提供一些 Validation e (a, b, c)
类型的值,或者根本不提供任何值:你可能已经注意到 applicative
定义中的 const
,只有类型论点的重要性:
main :: IO ()
main = quickBatch $ applicative (undefined :: Validation String (Int, Double, Char))
之后您可以 运行 测试并获得格式正确的结果。但是不,你不应该以这种方式测试应用程序。
为什么不应该
checkers
提供的测试远远不够。根据 GHC 的 运行 时间单态性质及其处理歧义的方式,您必须向 运行 测试 Validation String (Int, Double, Char)
提供四种具体的非多态类型,测试模块将仅针对这四种类型生成和测试,而您的应用函子应与满足上下文的任何类型一起使用。
IMO 大多数多态函数都不能很好地适合单元测试框架:它不能针对所有可能的类型进行测试,因此必须选择要么只使用手动选择的类型进行一些测试,要么进行测试在足够通用的类型上(例如当您的代码需要任意 monad 时的 Free monad,但通常 "general enough" 在其他上下文中定义不明确)。
你最好严格检查你的实现,并证明所有的法则都满足所有的情况,无论是用纸笔还是用像 agda 这样的证明引擎。这是 Maybe
上的一个示例,可能会有所帮助:Proving Composition Law for Maybe Applicative
编辑:请阅读评论。我并不完全理解它,但这意味着 Integer
是用于单元测试多态函数的 "general enough" 类型。我发现 This blog article by Bartosz Milewski 及其参考书目是掌握参数性和自由定理思想的好资源。
Checkers is a library for reusable QuickCheck properties, particularly for standard type classes
如何编写跳棋实例来测试我的验证应用实例是否有效?
import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes
import Control.Applicative
import Data.Monoid
data Validation e a =
Error e
| Scss a
deriving (Eq, Show)
instance Functor (Validation e) where
fmap _ (Error e) = Error e
fmap f (Scss a) = Scss $ f a
instance Monoid e => Applicative (Validation e) where
pure = Scss
(<*>) (Scss f) (Scss a) = Scss $ f a
(<*>) (Error g) (Scss a) = Error g
(<*>) (Scss a) (Error g) = Error g
(<*>) (Error a) (Error g) = Error $ mappend a g
instance (Arbitrary a, Arbitrary b) => Arbitrary (Validation a b) where
arbitrary = do
a <- arbitrary
b <- arbitrary
elements [Scss a, Error b]
instance (Eq a, Eq b) => EqProp (Validation a b) where (=-=) = eq
main :: IO ()
main = quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
我想我快到了,但我收到如下错误:
chap17/Validation_applicative.hs:36:21: No instance for (CoArbitrary (Validation e0 [Char])) …
arising from a use of ‘applicative’
In the second argument of ‘($)’, namely
‘applicative [(Scss "b", Scss "a", Scss "c")]’
In the expression:
quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
In an equation for ‘main’:
main = quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
Compilation failed.
我试过像这样为验证添加 CoArbitrary 实例:
instance CoArbitrary (Validation a b)
但这会导致此错误消息:
chap17/Validation_applicative.hs:35:10: No instance for (GHC.Generics.Generic (Validation a b)) …
arising from a use of ‘Test.QuickCheck.Arbitrary.$gdmcoarbitrary’
In the expression: Test.QuickCheck.Arbitrary.$gdmcoarbitrary
In an equation for ‘coarbitrary’:
coarbitrary = Test.QuickCheck.Arbitrary.$gdmcoarbitrary
In the instance declaration for ‘CoArbitrary (Validation a b)’
chap17/Validation_applicative.hs:38:21: No instance for (Eq e0) arising from a use of ‘applicative’ …
The type variable ‘e0’ is ambiguous
Note: there are several potential instances:
instance Eq a => Eq (Const a b) -- Defined in ‘Control.Applicative’
instance Eq a => Eq (ZipList a) -- Defined in ‘Control.Applicative’
instance Eq a => Eq (Data.Complex.Complex a)
-- Defined in ‘Data.Complex’
...plus 65 others
In the second argument of ‘($)’, namely
‘applicative [(Scss "b", Scss "a", Scss "c")]’
In the expression:
quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
In an equation for ‘main’:
main = quickBatch $ applicative [(Scss "b", Scss "a", Scss "c")]
Compilation failed.
如何
要自动派生 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
和 c
,此函数将创建一堆 m
- - 作为应用函子 -- 应该满足,稍后您可以使用 QuickCheck
生成的随机 a
b
c
值来测试它们。 [(Scss "b", Scss "a", Scss "c")]
具有类型 [(Validation String, Validation String, Validation String)]
使得 m ~ []
.
所以你应该提供一些 Validation e (a, b, c)
类型的值,或者根本不提供任何值:你可能已经注意到 applicative
定义中的 const
,只有类型论点的重要性:
main :: IO ()
main = quickBatch $ applicative (undefined :: Validation String (Int, Double, Char))
之后您可以 运行 测试并获得格式正确的结果。但是不,你不应该以这种方式测试应用程序。
为什么不应该
checkers
提供的测试远远不够。根据 GHC 的 运行 时间单态性质及其处理歧义的方式,您必须向 运行 测试 Validation String (Int, Double, Char)
提供四种具体的非多态类型,测试模块将仅针对这四种类型生成和测试,而您的应用函子应与满足上下文的任何类型一起使用。
IMO 大多数多态函数都不能很好地适合单元测试框架:它不能针对所有可能的类型进行测试,因此必须选择要么只使用手动选择的类型进行一些测试,要么进行测试在足够通用的类型上(例如当您的代码需要任意 monad 时的 Free monad,但通常 "general enough" 在其他上下文中定义不明确)。
你最好严格检查你的实现,并证明所有的法则都满足所有的情况,无论是用纸笔还是用像 agda 这样的证明引擎。这是 Maybe
上的一个示例,可能会有所帮助:Proving Composition Law for Maybe Applicative
编辑:请阅读评论。我并不完全理解它,但这意味着 Integer
是用于单元测试多态函数的 "general enough" 类型。我发现 This blog article by Bartosz Milewski 及其参考书目是掌握参数性和自由定理思想的好资源。