+{-# LANGUAGE TypeFamilies #-}

+{-# LANGUAGE FlexibleContexts #-}

+{-# LANGUAGE FlexibleInstances #-}

+-- All properties in this library are polymorphic. Because of it

+-- following expression will be rejected by compiler (assuming that

+-- @someFunction@ is polymorphic.

+-- > quickCheck (prop_Associative someFunction)

+-- Method for fixing type is nessesary.

+-- /Comparig for equality/

+-- 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.Common (

+ -- * Comparison for equality

+import Data.Function (on)

+-- | Values to be compared for equality

+data Equal a = Equal a a

+-- | Perform comparison using custom function

+appEqual :: (a -> a -> Bool) -> Equal a -> Bool

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

+-- | 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

+-- FIXME: Equalable is not composable. No way to compose two

+instance Equalable [Equal a] where

+ type Result [Equal a] = a

+ type Compared [Equal a] = Bool

+ equalWith = all . appEqual

+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