Haskell 如何在应用程序上测试多态函数?
我刚刚(对于Haskell 如何在应用程序上测试多态函数?,haskell,applicative,quickcheck,Haskell,Applicative,Quickcheck,我刚刚(对于数据.Sequence) 谁应该服从 traverseWithIndex f = sequenceA . mapWithIndex f 谢天谢地,这是对mapWithIndex源代码的直接机械修改,因此我很有信心它是正确的。然而,在更复杂的情况下,需要进行彻底的测试。我正在尝试编写一个QuickCheck属性来测试这个简单的属性。显然,我不能用每个应用程序的函子来尝试它!在测试幺半群时,使用某种类型的自由幺半群(即有限列表)进行测试是很有意义的。所以,在这里用一些函子进行测试似乎是
数据.Sequence
)
谁应该服从
traverseWithIndex f = sequenceA . mapWithIndex f
谢天谢地,这是对mapWithIndex
源代码的直接机械修改,因此我很有信心它是正确的。然而,在更复杂的情况下,需要进行彻底的测试。我正在尝试编写一个QuickCheck属性来测试这个简单的属性。显然,我不能用每个应用程序的
函子来尝试它!在测试幺半群时,使用某种类型的自由幺半群(即有限列表)进行测试是很有意义的。所以,在这里用一些函子进行测试似乎是明智的。有两个困难:
Eq
实例应用程序的
函子来尝试它
我想起了这一系列博客文章,但我并不完全理解:
Const
Identity
(>)
Applicative
函子都进行尝试,但您可以利用QuickCheck属性中的归纳参数来获得信心,即您的函数适用于大型归纳定义的函子族。例如,您可以测试:
- 您的函数适用于您选择的“原子”应用程序李>
- 如果您的函数对函子
和f
正常工作,则它对g
、Compose f g
和Product f g
正常工作Coproduct f g
Eq
实例
嗯,我想你可能需要看看函数相等性的快速检查测试。上一次我不得不按照这些思路做一些事情,我使用了Conal的库,它有“可以测试相等性的[t]类型的值,可能通过随机抽样。”这应该已经给了你一个想法,即使你没有函数的Eq
实例,QuickCheck
可能能够证明两个函数不相等。关键的是,这个实例存在:
instance (Show a, Arbitrary a, EqProp b) => EqProp (a -> b)
…任何具有Eq
实例的类型都有一个平凡的EqProp
实例,其中(=-=)=(==)
在我看来,这意味着使用Coyoneda Something
作为基本函子,并找出如何将所有小函数连接在一起。这里有一个部分(?)解决方案。我们要检查的主要方面是1)显然计算的值相同,2)效果的执行顺序相同。我认为以下代码是不言自明的:
{-# LANGUAGE FlexibleInstances #-}
module Main where
import Control.Applicative
import Control.Applicative.Free
import Data.Foldable
import Data.Functor.Identity
import Test.QuickCheck
import Text.Show.Functions -- for Show instance for function types
data Fork a = F a | G a deriving (Eq, Show)
toIdentity :: Fork a -> Identity a
toIdentity (F a) = Identity a
toIdentity (G a) = Identity a
instance Functor Fork where
fmap f (F a) = F (f a)
fmap f (G a) = G (f a)
instance (Arbitrary a) => Arbitrary (Fork a) where
arbitrary = elements [F,G] <*> arbitrary
instance (Arbitrary a) => Arbitrary (Ap Fork a) where
arbitrary = oneof [Pure <$> arbitrary,
Ap <$> (arbitrary :: Gen (Fork Int)) <*> arbitrary]
effectOrder :: Ap Fork a -> [Fork ()]
effectOrder (Pure _) = []
effectOrder (Ap x f) = fmap (const ()) x : effectOrder f
value :: Ap Fork a -> a
value = runIdentity . runAp toIdentity
checkApplicative :: (Eq a) => Ap Fork a -> Ap Fork a -> Bool
checkApplicative x y = effectOrder x == effectOrder y && value x == value y
succeedingExample = quickCheck (\f x -> checkApplicative
(traverse (f :: Int -> Ap Fork Int) (x :: [Int]))
(sequenceA (fmap f x)))
-- note reverse
failingExample = quickCheck (\f x -> checkApplicative
(traverse (f :: Int -> Ap Fork Int) (reverse x :: [Int]))
(sequenceA (fmap f x)))
-- instance just for example, could make a more informative one
instance Show (Ap Fork Int) where show _ = "<Ap>"
-- values match ...
betterSucceedingExample = quickCheck (\x ->
value (sequenceA (x :: [Ap Fork Int]))
== value (fmap reverse (sequenceA (reverse x))))
-- but effects don't.
betterFailingExample = quickCheck (\x -> checkApplicative
(sequenceA (x :: [Ap Fork Int]))
(fmap reverse (sequenceA (reverse x))))
{-#语言灵活实例}
模块主要在哪里
导入控制
导入控制.Applicative.Free
导入数据。可折叠
导入Data.Functor.Identity
导入测试。快速检查
import Text.Show.Functions--用于函数类型的Show实例
数据分叉a=fa | ga推导(等式,显示)
toIdentity::Fork a->Identity a
身份(F a)=身份a
toIdentity(ga)=恒等式a
实例函子Fork,其中
fmap f(FA)=f(FA)
fmap f(GA)=G(FA)
实例(任意a)=>任意(Fork a),其中
任意=元素[F,G]任意
实例(任意a)=>任意(Ap Fork a),其中
任意的,任意的,
Ap(任意::Gen(Fork Int))任意]
effectOrder::Ap Fork a->[Fork()]
有效顺序(纯=
效应顺序(Ap x f)=fmap(const())x:effectOrder f
值::Ap Fork a->a
value=runIdentity。不一致性
勾选应用::(等式a)=>Ap分叉a->Ap分叉a->Bool
checkApplicative x y=effectOrder x==effectOrder y&&value x==value y
succeedingExample=quickCheck(\f x->checkApplicative
(遍历(f::Int->Ap Fork Int)(x::[Int]))
(序列A(fmap f x)))
--钞票背面
failingExample=quickCheck(\f x->checkApplicative
(遍历(f::Int->Ap Fork Int)(反向x::[Int]))
(序列A(fmap f x)))
--举个例子,可以做一个信息量更大的例子
实例显示(Ap Fork Int),其中显示
--值匹配。。。
betterSucceedingExample=快速检查(\x->
值(sequenceA(x::[Ap Fork Int]))
==值(fmap反向(序列A(反向x)))
--但效果并不如此。
betterFailingExample=quickCheck(\x->CheckApplication)
(sequenceA(x::[Ap Fork Int]))
(fmap反向(序列A(反向x)))
输出如下所示:
*Main Text.Show.Functions> succeedingExample
+++ OK, passed 100 tests.
*Main Text.Show.Functions> failingExample
*** Failed! Falsifiable (after 3 tests and 2 shrinks):
<function>
[0,1]
*Main Text.Show.Functions> betterSucceedingExample
+++ OK, passed 100 tests.
*Main Text.Show.Functions> betterFailingExample
*** Failed! Falsifiable (after 10 tests and 1 shrink):
[<Ap>,<Ap>]
*主文本.Show.Functions>成功示例
+++好的,通过了100次测试。
*主文本.Show.Functions>failingExample
***失败了!可伪造(经过3次测试和2次收缩后):
[0,1]
*Main Text.Show.Functions>betterSucceedingExample
+++好的,通过了100次测试。
*Main Text.Show.Functions>betterFailingExample
***失败了!可伪造(10次测试和1次收缩后):
[,]
啊,很有趣。Fork
在这里是否提供了比other
更多的功能?它的种类与other
不同。也就是说,您可以使用明显的(?)任意实例来执行标记为a=标记为String a的数据,而不是Fork
。从技术上讲,我不认为这会增加歧视性权力,但它可能会使反例更容易
*Main Text.Show.Functions> succeedingExample
+++ OK, passed 100 tests.
*Main Text.Show.Functions> failingExample
*** Failed! Falsifiable (after 3 tests and 2 shrinks):
<function>
[0,1]
*Main Text.Show.Functions> betterSucceedingExample
+++ OK, passed 100 tests.
*Main Text.Show.Functions> betterFailingExample
*** Failed! Falsifiable (after 10 tests and 1 shrink):
[<Ap>,<Ap>]