2014-10-17 10 views
3

HSpec 및 QuickCheck를 사용하여 Monoids (연관성 및 신원 요소)의 등록 정보를 확인하려고합니다. 특정 인스턴스를 확인하려고하지만 대부분의 코드를 다형성으로 유지하려고합니다. (그래도 난하고 싶은 무엇HSpec 및 QuickCheck을 사용하여 Data.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 

과 실제 모노 이드 예 (합계, 제품)과 유형을 지정을 다형성 지능 : 이것은 내가 몇 시간 후 해낸 것입니다 , Double) 나중에. 문제는 유형 검사를하지 않는다는 것입니다. 나는 계속 간다.

src/[email protected]: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/[email protected]: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에 대한 스펙을 표현하고 코드 중복을 피하는 방법입니다.

+0

[hspec-laws] (https://github.com/hspec/hspec-laws#readme) 또는 [hspec-checkers] (http://hackage.haskell.org/package/)를 사용하는 것이 좋습니다. hspec-checkers)를 사용하십시오. –

답변

3

종류는 property :: Testable prop => prop -> Property입니다. 유형 변수 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 만 있으면됩니다.

+0

나는 당신의 솔루션으로 약간 놀았고 (틀렸다면 고쳐 줘야한다.) 어쨌든 첫 번째 인자를 무시하고 타입 정보가 이미 존재하기 때문에 실제로 propertyP가 필요하지 않은 것처럼 보인다. propertyP를 꺼낸 후 프록시 버전은 Tagged와 거의 비슷하게 보입니다. 프록시를 사용하면 조금 더 좋아 보이는 호출 사이트 일뿐입니다. – maciekszajna

+0

엄밀히 말하면,'propertyP'는 필요 없지만, 1 번째와 2 번째의 인수가 같은 형태를 가지기 (위해) 때문에, 때로는 명시적인 형태의 서명을 기입 할 필요가 없어집니다. – user2407038