0

I have written a function in Haskell that takes a list of arbitrary elements and returns (mapped) a list of tuples. Each tuple contains the original element and a fraction, with all the fractions in the list adding to 1 (therefore I simply calculate the fraction once using 1 ``div`` length xs and apply it all elements). This is the code:

uniform :: [a] -> [(a, Int)]
uniform xs = map (\x -> (x, prob)) xs
  where prob = 1 `div` (length xs)

(Disclaimer: This is actually a slightly simplified version, but I am producing the exact same behaviour, so hopefully this suffices).

I am trying to cover this with a property based test using Hspec and Quickcheck:

spec = do
    describe "uniform" $ do

        it "produces a uniform distribution summing to 1" $ property $
            let totalProbability ((_, p):xs) = p + (totalProbability xs)
            in (\xs -> (totalProbability $ uniform xs) `shouldBe` 1)

However when I run this I get this error:

• Ambiguous type variable ‘a0’ arising from a use of ‘property’
  prevents the constraint ‘(Arbitrary a0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘a0’ should be.
  These potential instances exist:
    instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b)
      -- Defined in ‘Test.QuickCheck.Arbitrary’
    instance Arbitrary Ordering
      -- Defined in ‘Test.QuickCheck.Arbitrary’
    instance Arbitrary Integer
      -- Defined in ‘Test.QuickCheck.Arbitrary’
    ...plus 19 others
    ...plus 62 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the second argument of ‘($)’, namely
    ‘property
       $ let totalProbability ((_, p) : xs) = p + (totalProbability xs)
         in (\ xs -> (totalProbability $ uniform xs) `shouldBe` 1)’
  In a stmt of a 'do' block:
    it "produces a uniform distribution summing to 1"
      $ property
          $ let totalProbability ((_, p) : xs) = p + (totalProbability xs)
            in (\ xs -> (totalProbability $ uniform xs) `shouldBe` 1)
  In the second argument of ‘($)’, namely
    ‘do it "produces a uniform distribution summing to 1"
          $ property
              $ let totalProbability ((_, p) : xs) = ...
                in (\ xs -> (totalProbability $ uniform xs) `shouldBe` 1)’

| 12 | it "produces a uniform distribution summing to 1" $ property $ | ^^^^^^^^^^...

I am guessing that somewhere I have not given QuickCheck enough information about how to generate the test value, but I am not sure where to go from here.

Any help would be greatly appreciated.

Thanks!

Raiden616
  • 1,545
  • 3
  • 18
  • 42

1 Answers1

1

You need to specify a type for xs: is that a list of strings? ints? booleans? This is so that QuickCheck can generate a random sample in that type.

You can write, for instance:

...
in (\xs -> (totalProbability $ uniform (xs :: [Int])) `shouldBe` 1)
chi
  • 111,837
  • 3
  • 133
  • 218
  • Right, okay I thought it might be something exactly like that. The issue is I want this function to apply to an xs of any type. Since the map just wraps it up it really doesn't matter what type it is. Is this not something quickcheck deals with? In which case do I need to provide multiple properties for different types, or can I write some kind of custom generator? – Raiden616 Oct 14 '18 at 16:03
  • @Raiden616 QuickCheck does not know the type does not matter. In this case, you can see that `uniform` only adds uniform weights, and `totalProbability` sums those weights neglecting the data they are paired with. So, indeed the type does not matter. (Even without looking at the `uniform` code, one might actually prove that using parametricity, and a bit of theoretical computer science). In this case, you can test for `[Int]` only, since if your code works for that type, it will work for any other type as well. There's no need to use QuickCheck to "generate a random type", so to speak. – chi Oct 14 '18 at 16:14