+{-# LANGUAGE TypeFamilies #-}

+{-# LANGUAGE FlexibleContexts #-}

+-- A lot of QuickCheck properties have form @expression = another

+-- expression@. It's natural to compare them for equality however not

+-- all types have 'Eq' instance. For example functions and hence many

+-- There are three generic ways to compare values for equality.

+-- (1) Use '==' operator

+-- 2. Convert value to some type with Eq instance and compare

+-- them. Caller must ensure that such conversion make sence

+-- 3. Most generic: use custom comparison function.

+-- Functions 'eq', 'eqOn' and 'eqWith' provide this functionality.

+module Test.QuickCheck.Property.Utils (

+ -- * Comparison for equality

+import Data.Function (on)

+-- | Values to be compared for equality

+data Equal a = Equal a a

+-- | Recurse through function to apply comparison to 'Equal'.

+ -- | Type which should be compared for equality

+ -- | Result of comparison

+ equalWith :: (Result a -> Result a -> Bool) -> a -> Compared a

+instance Equalable (Equal a) where

+ type Result (Equal a) = a

+ type Compared (Equal a) = Bool

+ equalWith f (Equal a b) = f a b

+instance Equalable a => Equalable (x -> a) where

+ type Result (x -> a) = Result a

+ type Compared (x -> a) = x -> Compared a

+ equalWith f fun = equalWith f . fun

+-- | Compare values using @==@

+eq :: (Equalable a, Eq (Result a)) => a -> Compared a

+-- | Convert values to types which could be compare

+eqOn :: (Equalable a, Eq b) => (Result a -> b) -> a -> Compared a

+eqOn f = equalWith ((==) `on` f)

+-- | Compare with custom function. Just a shorter sinonym for equalWith

+eqWith :: (Equalable a) => (Result a -> Result a -> Bool) -> a -> Compared a

+-- | Data type is used to fix concrete data in properties