2015-11-11 261 views
3

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

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 

回答

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返回函数(+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)类型的属性,那么您将很抱歉!

+0

这看起来像我想要的。我是新来的哈斯克尔,并试图找出如何将这些依赖关系添加到我的单个文件,使用堆栈。没有yaml或cabal文件。当我明白这一点时,我会在你证明它的工作时标记你的答案并且赞成。感谢您的详细解答。 – mac10688

+0

@ mac10688如果您未使用堆栈文件或cabal文件,则需要使用'cabal install'安装软件包。您需要使用堆栈文件或cabal文件来显式依赖。 –

+0

@DavidYoung我能够使用stack ghci --resolver nightly-2015-11-10 --package universe QuickCheck。现在,我正在处理一些编译错误。 – mac10688