0

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))
  • This code looks a bit like a cross between C and Lisp. Using `-1` to represent an invalid value is really not idiomatic, and using partial functions like `tail` is discouraged in situations where pattern matching can easily do the job. – dfeuer Apr 20 '21 at 00:02
  • I just started learning Haskell and actually, this is the first time learning a functional programming language so the code surely looks rusty for those who worked with it a lot. I've posted the instance Num Numar_Unar – mariciucioan Apr 20 '21 at 00:37
  • `arbitrary = elements [Invalid, Unar []]` means "when I run `arbitrary` on this type, always give me `Invalid` or `Unar []`". Why did you write that if that's not what you want? – Joseph Sible-Reinstate Monica Apr 20 '21 at 00:43
  • Thank you for your answer! I understood what that means... but I can't figure out how to make that when I run arbitrary to give me `Invalid` or a random `Unar` number.. What function should I use or how should I try to generate that list of `Unar`? – mariciucioan Apr 20 '21 at 00:51

1 Answers1

2

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]