gpt4 book ai didi

haskell - 使用 HSpec 和 QuickCheck 验证 Data.Monoid 属性

转载 作者:行者123 更新时间:2023-12-04 20:05:14 26 4
gpt4 key购买 nike

我正在尝试使用 HSpec 和 QuickCheck 来验证 Monoids 的属性(关联性和标识元素)。我将验证特定实例,但希望保留大部分代码的多态性。这是我几个小时后想到的:

module Test where

import Test.Hspec
import Test.QuickCheck
import Data.Monoid

instance (Arbitrary a) => Arbitrary (Sum a) where
arbitrary = fmap Sum arbitrary

instance (Arbitrary a) => Arbitrary (Product a) where
arbitrary = fmap Product arbitrary

prop_Monoid_mappend_mempty_x x = mappend mempty x === x

sumMonoidSpec = it "mappend mempty x = x" $ property (prop_Monoid_mappend_mempty_x :: Sum Int -> Property)
productMonoidSpec = it "mappend mempty x = x" $ property (prop_Monoid_mappend_mempty_x :: Product Double -> Property)

main :: IO ()
main = hspec $ do
describe "Data.Monoid.Sum" $ do
sumMonoidSpec
describe "Data.Monoid.Product" $ do
productMonoidSpec

我想要的是多态
monoidSpec = it "mappend mempty x = x" $ property prop_Monoid_mappend_mempty_x

并稍后指定实际的 Monoid 实例(Sum、Product)和类型(Int、Double)。问题是它不会键入检查。我不断得到
src/Test.hs@18:42-18:50 No instance for (Arbitrary a0) arising from a use of property
The type variable a0 is ambiguous
Note: there are several potential instances:
instance Arbitrary a => Arbitrary (Product a)
-- Defined at /home/app/isolation-runner-work/projects/68426/session.207/src/src/Test.hs:10:10
instance Arbitrary a => Arbitrary (Sum a)
-- Defined at /home/app/isolation-runner-work/projects/68426/session.207/src/src/Test.hs:7:10
instance Arbitrary () -- Defined in Test.QuickCheck.Arbitrary
...plus 27 others …
src/Test.hs@18:51-18:79 No instance for (Monoid a0)
arising from a use of prop_Monoid_mappend_mempty_x
The type variable a0 is ambiguous
Note: there are several potential instances:
instance Monoid () -- Defined in Data.Monoid
instance (Monoid a, Monoid b) => Monoid (a, b)
-- Defined in Data.Monoid
instance (Monoid a, Monoid b, Monoid c) => Monoid (a, b, c)
-- Defined in Data.Monoid
...plus 18 others …

我知道我需要将多态版本中的 Monoid 约束为任意、Eq 和 Show,但我不知道如何。

问题是如何以多态的方式表达 Monoid 的规范并避免代码重复?

最佳答案

注意property :: Testable prop => prop -> Property的类型.类型 var prop被删除,如果类型变量不再可用,则无法进行实例解析。基本上你想要做的是推迟实例选择,为此你必须使类型可用,直到你选择实例的那一刻。

一种方法是随身携带一个额外的 Proxy prop范围:

-- Possibly Uuseful helper function
propertyP :: Testable prop => Proxy prop -> prop -> Property
propertyP _ = property

monoidProp :: forall m . (Arbitrary m, Testable m, Show m, Monoid m, Eq m)
=> Proxy m -> Property
monoidProp _ = property (prop_Monoid_mappend_mempty_x :: m -> Property)

monoidSpec :: (Monoid m, Arbitrary m, Testable m, Show m, Eq m) => Proxy m -> Spec
monoidSpec x = it "mappend mempty x = x" $ monoidProp x

main0 :: IO ()
main0 = hspec $ do
describe "Data.Monoid.Sum" $ do
monoidSpec (Proxy :: Proxy (Sum Int))
describe "Data.Monoid.Product" $ do
monoidSpec (Proxy :: Proxy (Product Double))

另一种方法是使用像 tagged 这样的库。提供类型 Tagged ,它只是向现有类型添加一些幻影类型参数:
import Data.Tagged

type TaggedProp a = Tagged a Property
type TaggedSpec a = Tagged a Spec

monoidPropT :: forall a. (Monoid a, Arbitrary a, Show a, Eq a)
=> TaggedProp a
monoidPropT = Tagged (property (prop_Monoid_mappend_mempty_x :: a -> Property))

monoidSpecT :: forall a . (Monoid a, Arbitrary a, Show a, Eq a) => TaggedSpec a
monoidSpecT = Tagged $ it "mappend mempty x = x"
(unTagged (monoidPropT :: TaggedProp a))

main1 :: IO ()
main1 = hspec $ do
describe "Data.Monoid.Sum" $ do
untag (monoidSpecT :: TaggedSpec (Sum Int))
describe "Data.Monoid.Product" $ do
untag (monoidSpecT :: TaggedSpec (Product Double))

这些解决方案本质上是等效的,尽管在某些情况下,其中一个可能更方便。由于我对您的用例还不够了解,因此我将两者都包括在内。

这两个都只需要 -XScopedTypeVariables .

关于haskell - 使用 HSpec 和 QuickCheck 验证 Data.Monoid 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26428326/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com