Search code examples
haskellquickcheck

Make similar QuickCheck properties DRY


I have a bunch of QuickCheck properties defined as follows:

 ...
 prop_scaleData3 d n = n > 1 ⇛ length (scaleData d n) ≡ n
 prop_scaleData4 d n = n > 1 ⇛ head (scaleData d n) ≡ -d
 prop_scaleData5 d n = n > 1 ⇛ last (scaleData d n) ≡ d 
 ...     

That's a lot of repetition. What would be the right way to DRY it up?


Solution

  • prop_scaleData3 d n = n > 1 ==> length (scaleData d n) == n
    prop_scaleData4 d n = n > 1 ==> head (scaleData d n) == -d
    prop_scaleData5 d n = n > 1 ==> last (scaleData d n) == d 
    

    Just observe what is similar about these three functions and make a new helper function that extracts out the commonalities. For example:

    scaleProp :: Int -> Int -> ([Int] -> Int) -> Int -> Bool
    scaleProp d n op res = n > 1 ==> op (scaleData d n) == res
    

    Then you can express your original props in terms of the helper:

     prop_scaleData3 d n = scaleProp d n length n
     prop_scaleData4 d n = scaleProp d n head   (-d)
     prop_scaleData4 d n = scapeProp d n last   d
    

    At this point the repetition isn't about logic so much as syntax (naming functions and applying arguments). In such cases I don't feel the DRY principle is really helpful - you can make less syntactic repetition but you'll lose readability or modularity. For example, Toxaris combined the solutions into one function; we can do the same but lets do it in a simpler way with just lists of booleans:

    prop_scaleData345 d n =
      let sp = scaleProp d n
      in and [sp length n, sp head (-d), sp last d]
      -- or instead:
      -- in all (uncurry sp) [(length, n), (head, negate d), (last, d)]