如何使用检查器测试此应用程序实例? (没有CoArbitrary的实例(验证e0 [Char]))

时间:2016-03-15 10:59:31

标签: haskell quickcheck

  

Checkers是一个可重用的QuickCheck属性库,特别适用于   标准类型

如何编写检查器实例以测试我的应用验证实例是否有效?

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.

1 个答案:

答案 0 :(得分:8)

如何

要自动派生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

简而言之,给定了abcm这四种类型,此函数将创建一组a - 作为属性的属性应用仿函数 - 应该满足,之后您可以使用b生成的随机c QuickCheck [(Scss "b", Scss "a", Scss "c")]值来测试它们。 [(Validation String, Validation String, Validation String)]类型m ~ []生成Validation e (a, b, c)

因此,您应该提供const类型的某些值,或者根本没有值:您可能已经注意到applicative的定义中的main :: IO () main = quickBatch $ applicative (undefined :: Validation String (Int, Double, Char)) ,只有checkers的类型争论很重要:

Validation String (Int, Double, Char)

之后,您可以运行测试并获得格式良好的结果。但不,你不应该以这种方式测试一个应用程序。

为什么不

Maybe提供的测试远远不够。通过GHC的运行时单形性以及它如何处理歧义,你必须提供四个具体的非多态类型来运行像Integer这样的测试,测试模块将只生成和测试这四种类型,而您的applicative functor应该使用符合上下文的任何类型。

IMO大多数多态函数都不适合单元测试框架:它不能针对所有可能的类型进行测试,因此必须选择仅使用手选类型进行一些测试,或者进行测试键入足够通用(如你的代码需要任意monad时的Free monad,但通常&#34;足够通用&#34;在其他上下文中没有明确定义)。

您可以更严格地检查您的实施情况,并证明所有法律都满足所有法律要求,无论是使用笔和纸,还是使用像agda这样的证明引擎。以下是myGamePiece = new component(30, 30, "Pacman.png", 10, 210, "image"); 上可能有所帮助的示例:Proving Composition Law for Maybe Applicative

编辑:请阅读评论。我并没有完全理解它,但这意味着<img id="green" src="PacmanGreen.png" draggable="true" ondragstart="drag(event)" width="30" height="30"> 是足够的&#34;&#34;用于单元测试多态函数的类型。我发现This blog article by Bartosz Milewski及其参考书目是抓住参数化和自由定理的好资源。