如何为包装函数的类型编写任意实例?

How to write an Arbitrary instance for a type that wraps a function?

我有这种包装函数的类型

newtype Transaction d e r = Transaction (d -> Either e (d,r))

...我想对其 Functor 和 Applicative 实例进行快速检查测试,但编译器抱怨它没有 Arbitrary 实例。

我尝试这样做,但我一直无法生成随机函数。

谢谢!

==更新==

quickcheck 属性定义如下

type IdProperty f a = f a -> Bool
functorIdProp :: (Functor f, Eq (f a)) => IdProperty f a
functorIdProp x = (fmap id x) == id x

type CompositionProperty f a b c = f a -> Fun a b -> Fun b c -> Bool
functorCompProp :: (Functor f, Eq (f c)) => CompositionProperty f a b c
functorCompProp x (apply -> f) (apply -> g) = (fmap (g . f) x) == (fmap g . fmap f $ x)

instance (Arbitrary ((->) d  (Either e (d, a)))) => Arbitrary (DbTr d e a) where
    arbitrary = do
      f <- ...???
      return $ Transaction f

...测试如下所示:

spec = do
  describe "Functor properties for (Transaction Int String)" $ do
    it "IdProperty (Transaction Int String) Int" $ do
      property (functorIdProp :: IdProperty (Transaction Int String) Int)

    it "CompositionProperty (Transaction Int String) Int String Float" $ do
      property (functorCompProp :: CompositionProperty (Transaction Int String) Int String Float)

你应该使用 Test.QuickCheck.Function wrapper to test functions. It seems to have no sense in having Arbitrary or CoArbitrary instances for Transaction if you only need to test type class laws for your Transaction type (but if you really need it, you can find ).

要测试定律,您可以这样写属性:

{-# LANGUAGE DeriveFunctor #-}

import Test.QuickCheck
import Test.QuickCheck.Function

newtype Transaction d e r = Transaction (d -> Either e (d,r))
  deriving (Functor)

-- fmap id ≡ id
prop_transactionFunctorId :: Int
                          -> Fun Int (Either Bool (Int,String)) 
                          -> Bool
prop_transactionFunctorId d (Fun _ f) = let t              = Transaction f
                                            Transaction f' = fmap id t
                                        in f' d == f d

嗯,这可能看起来不像你想要的那么漂亮。但这是测试任意函数的好方法。比如我们可以把最后一行in f' d == f d替换成in f' d == f 1,看看如果失败了怎么办:

ghci> quickCheck prop_transactionFunctorId 
*** Failed! Falsifiable (after 2 tests and 4 shrinks):    
0
{0->Left False, _->Right (0,"")}