I have 2 data types that I use for the representation of natural numbers in the unary system
data Valoare_Unar = Unu deriving(Show);
data Numar_Unar = Invalid | Unar [Valoare_Unar] deriving(Show);
I have those functions for converting this number from Integer to my data type and vice-versa.
convert :: Unar.Numar_Unar -> Integer
convert Unar.Invalid = (-1)
convert (Unar.Unar xs)
| null xs = 0
| otherwise = 1 + convert (Unar.Unar (tail xs))
convert' :: Integer -> Unar.Numar_Unar
convert' (-1) = Unar.Invalid
convert' x = fromInteger x :: Unar.Numar_Unar
I want to test that (convert' (convert _)) == _
and I have
prop_check xs = Utils.convert' (Utils.convert xs) == xs where types = xs::Unar.Numar_Unar
for that.
I've made my data types instances of Arbitrary but I had problems figuring out how I should generate values for the "Unar" constructor.
instance Arbitrary Valoare_Unar where
arbitrary = elements [Unu]
instance Arbitrary Numar_Unar where
arbitrary = elements [Invalid, Unar []]
Using QC.verboseCheck prop_check
I've noticed that the only test values are "Invalid" and "Unar[]".
My question is: How could I generate values like "Unar[Unu] ; Unar[Unu,Unu,Unu] ; ... ; Unar [Unu,..,Unu]" for testing ?
EDIT: instance Num Numar_Unar
instance Num Numar_Unar where
(+) (Unar xs) (Unar ys)
| null ys = (Unar xs)
| otherwise = (+) (Unar (xs ++ [head ys])) (Unar (tail ys))
(+) _ _ = Invalid
(-) (Unar xs) (Unar ys)
| length xs < length ys = Invalid
| null ys = Unar xs
| xs == ys = Unar []
| otherwise = (-) (Unar (tail xs)) (Unar (tail ys))
(-) _ _ = Invalid
(*) (Unar xs) (Unar ys)
| null ys = Unar []
| otherwise = (+) (Unar xs) ((*) (Unar xs) (Unar (tail ys)))
(*) _ _ = Invalid
abs (Unar xs) = Unar xs
abs Invalid = Invalid
signum (Unar xs)
| null xs = Unar []
| otherwise = Unar [Unu]
signum Invalid = Invalid
fromInteger x
| x < 0 = Invalid
| x == 0 = Unar []
| otherwise = (+) (Unar [Unu]) (fromInteger (x-1))
arbitrary = elements [Invalid, Unar []]
means "when I run arbitrary
on this type, always give me Invalid
or Unar []
". A proper Arbitrary
instance looks like this:
instance Arbitrary Numar_Unar where
arbitrary = frequency [(1, return Invalid), (3, Unar <$> arbitrary)]
This will make arbitrary
generate Invalid
1/4 of the time and Unar
3/4 of the time. When it generates Unar
, it will call arbitrary
for the [Valoare_Unar]
type to get a list with a random number of elements instead of always none. You should also consider adding a shrink
method to the class, like this:
shrink Invalid = []
shrink (Unar []) = [Invalid]
shrink (Unar (Unu:xs)) = [Invalid, Unar [], Unar xs]