Search code examples
haskellquickcheck

Simple QuickCheck Test Never Completes


I'm having a bit of trouble wrapping my mind around QuickCheck. Consider the following simple data type and it's corresponding arbitrary instance.

EDIT: Full code below as requested in a comment. fixedToFractional converts fixed numeric types such as Micro to Fractional.

import Data.Fixed
import System.Random
import Control.Applicative
import Test.QuickCheck

fixedToFractional :: (HasResolution a, Fractional b) => Data.Fixed.Fixed a -> b
fixedToFractional = fromRational . toRational

instance Random Micro where
    randomR (lo, hi) g = randomR (fixedToFractional lo, fixedToFractional hi) g
    random g           = randomR (-999999, 999999) g

data FooType = FooType { foo :: Micro } deriving (Show)

instance Arbitrary FooType where
    arbitrary =
        FooType <$> x
        where
            x = choose (0.0, 1.0)

If I understand things correctly, this should allow random FooType values to be generated, with the foo field set to a random value between 0 and 1.

Next, consider the following simple test.

prop_foo_is_gte_zero ft = (foo ft) >= 0.0

When I attempt to run the above test, whether in ghci, or in a test file using quickCheckAll, the test never completes. ghc eats up a few gigs of RAM in a matter of seconds, and will eventually run my machine out of swap space if I don't kill it. I'm probably missing something incredibly fundamental/stupid, but (obviously) don't know what. Any ideas?

EDIT: I am now questioning my Random instance. Perhaps the problem is there and not with my Arbitrary instance.


Solution

  • Seems you have an issue inside your Random instance for Micro

    Here is a quick hack to fix it:

    {-# LANGUAGE TypeSynonymInstances #-}
    {-# LANGUAGE FlexibleInstances #-}
    
    import Data.Fixed
    import System.Random
    import Test.QuickCheck
    
    toDouble :: Micro -> Double
    toDouble = realToFrac . toRational
    
    instance Random Micro where
        randomR (lo, hi) g =
          let (a,g') = randomR (toDouble lo, toDouble hi) g
          in (fromRational (toRational a), g')
        random = randomR (-999999, 999999)
    
    data FooType = FooType { foo :: Micro } deriving (Show)
    
    instance Arbitrary FooType where
        arbitrary =
            FooType <$> x
            where
                x = choose (0.0, 1.0)
    
    main :: IO ()
    main = quickCheck prop_foo_is_gte_zero
      where prop_foo_is_gte_zero ft = foo ft >= 0.0