Haskell高阶类型的任意实例

时间:2015-11-11 01:22:35

标签: haskell quickcheck

我有类型,它是SemiGroup的一个实例。 我想编写一个quickCheck方法来确保它是正确的。 如何创建此类型的任意实例?

newtype Combine a b =
  Combine { unCombine :: a -> b }

instance Semigroup b => Semigroup (Combine a b) where
  x <> y = Combine $ \n -> unCombine x n <> unCombine y n

instance (Q.Arbitrary a, Num a) => Q.Arbitrary (Combine a b) where
  arbitrary = do
    a <- Q.arbitrary
    return $ Combine (\n -> Sum(n+1)) a

1 个答案:

答案 0 :(得分:4)

最小修正是打开FlexibleInstances并写:

instance (Q.Arbitrary a, Num a) => Q.Arbitrary (Combine a (Sum a)) where
  arbitrary = do
    a <- Q.arbitrary :: Q.Gen ()
    return $ Combine (\n -> Sum(n+1))

然而,这有点令人不满意:我观察到a完全未使用(因此必须手动给出类型签名!),并且函数的分布有点无聊,因为它返回函数(+1)(最多为newtype包装)概率为1.(也许你想要return $ Combine (\n -> Sum (n+a))?但即使这样你可以用这种方式生成的函数类也有点无聊。我不确定你是什么真的意思是,我不会花很长时间的推测。)

如果你想做得对,你应该为每个输入产生一个随机输出。使用universe包非常方便:

import Data.Universe
import Data.Universe.Instances.Reverse
instance (Ord a, Finite a, Q.Arbitrary b) => Q.Arbitrary (Combine a b) where
  arbitrary = Combine <$> sequenceA (const Q.arbitrary)

作为附带好处,这不需要任何扩展。但是,您应该谨慎选择包含小域名的输入类型 - Combine Bool (Sum Int)应该没问题,甚至是Combine Word8 (Sum Word8),但如果您尝试在类似Combine Int (Sum Int)的类型上测试某个属性,那么会抱歉!