簡體   English   中英

使用HSpec和QuickCheck驗證Data.Monoid屬性

[英]Use HSpec and QuickCheck to verify Data.Monoid properties

我正在嘗試使用HSpec和QuickCheck來驗證Monoid的屬性(關聯性和標識元素)。 我將驗證特定的實例,但想保留大多數代碼的多態性。 這是幾個小時后我想到的:

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約束為Arbitrary,Eq和Show,但我不知道如何。

問題是如何以多態方式表達Monoid的規范並避免代碼重復?

注意property :: Testable prop => prop -> Property的類型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需要-XScopedTypeVariables

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM