Search code examples
haskellquickcheck

Generate triple (Network.HTTP.ResponseCode) using an instance of Arbitrary


I have a function that takes a ResponseCode from Network.HTTP. In order to test it with QuickCheck, I wanted to write an instance of Arbitrary for ResponseCode. (In case you don't know, a ResponseCode is just a triple of ints in that library: type ResponseCode = (Int, Int, Int)).

So I wrote something like this:

instance Arbitrary ResponseCode where
    arbitrary = triple ( elements [1..6] )
       where triple f = (f, f, f)

First of all, GHC complains that the way I'm using types is not standard haskell so I would have to use some compiler flags (which is not really what I want since I feel like there must be an easy solution for this simple problem without flags).

Secondly, my arbitrary function has the wrong type, which is pretty obvious. But then I really didn't figure out how to write a function that returns a triple with random Ints ranging from 1-6.

I would appreciate if somebody could help me out here.

Thank you.


Solution

  • First of all, there is already are these two instances:

    instance Arbitrary Int
    instance (Arbitrary a, Arbitrary b, Arbitrary c) =>
             Arbitrary (a, b, c)
    

    This means that (Int,Int,Int) is already an instance of Arbitrary. This means that the type synonym ResponseCode is already an instance. You cannot define and use a second instance.

    You could try using Test.QuickCheck.Gen.suchThat but I hypothesize that it will not work well in this case. If you can, I suggest using a newtype wrapper:

    import Test.QuickCheck
    import Network.HTTP.Base
    import Control.Applicative
    import System.Random
    
    newtype Arb'ResponseCode = Arb'ResponseCode { arb'ResponseCode :: ResponseCode }
      deriving (Show)
    
    instance Arbitrary Arb'ResponseCode where
      arbitrary = do
          responseCode <- (,,) <$> f <*> f <*> f
          return (Arb'ResponseCode responseCode)
        where f = elements [1..6]
    
    -- To use this you can call
    -- (fmap arb'ResponseCode arbitrary)
    -- instead of just (arbitrary)
    

    Or perhaps use the built-in (Int,Int,Int) instance and post-process the three elements with (succ . (mod 6)).