-- |
-- Module      : Test.Speculate.Function
-- Copyright   : (c) 2016-2019 Rudy Matela
-- License     : 3-Clause BSD  (see the file LICENSE)
-- Maintainer  : Rudy Matela <rudy@matela.com.br>
--
-- This module is part of Speculate.
--
-- If should import this if you want Speculate to treat functions as data
-- values.  This exports a Listable instance for functions an a facility to
-- define Eq and Ord instances for functions.
--
-- Please see the @fun@ and @monad@ examples from Speculate's source
-- repository for more details.
module Test.Speculate.Function
  ( funToList
  , areEqualFor
  , compareFor
  )
where

import Test.Speculate
import Test.LeanCheck.Function()
import Test.LeanCheck.Error (errorToNothing)
import Data.Function (on)

funToList :: Listable a => (a -> b) -> [Maybe b]
funToList :: (a -> b) -> [Maybe b]
funToList a -> b
f = (a -> Maybe b) -> [a] -> [Maybe b]
forall a b. (a -> b) -> [a] -> [b]
map (b -> Maybe b
forall a. a -> Maybe a
errorToNothing (b -> Maybe b) -> (a -> b) -> a -> Maybe b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
f) [a]
forall a. Listable a => [a]
list

-- | This function can be used to define an Eq instance for functions based on
--   testing and equality of returned values, like so:
--
-- > instance (Listable a, Eq b) => Eq (a -> b) where
-- >   (==) = areEqualFor 100
areEqualFor :: (Listable a, Eq b) => Int -> (a -> b) -> (a -> b) -> Bool
areEqualFor :: Int -> (a -> b) -> (a -> b) -> Bool
areEqualFor Int
n = [Maybe b] -> [Maybe b] -> Bool
forall a. Eq a => a -> a -> Bool
(==) ([Maybe b] -> [Maybe b] -> Bool)
-> ((a -> b) -> [Maybe b]) -> (a -> b) -> (a -> b) -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Int -> [Maybe b] -> [Maybe b]
forall a. Int -> [a] -> [a]
take Int
n ([Maybe b] -> [Maybe b])
-> ((a -> b) -> [Maybe b]) -> (a -> b) -> [Maybe b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> [Maybe b]
forall a b. Listable a => (a -> b) -> [Maybe b]
funToList)

-- | This function can be used to define an Ord instance for functions based on
--   testing and ordering of returned values, like so:
--
-- > instance (Listable a, Ord b) => Ord (a -> b) where
-- >   compare = compareFor 100
compareFor :: (Listable a, Ord b) => Int -> (a -> b) -> (a -> b) -> Ordering
compareFor :: Int -> (a -> b) -> (a -> b) -> Ordering
compareFor Int
n = [Maybe b] -> [Maybe b] -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ([Maybe b] -> [Maybe b] -> Ordering)
-> ((a -> b) -> [Maybe b]) -> (a -> b) -> (a -> b) -> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Int -> [Maybe b] -> [Maybe b]
forall a. Int -> [a] -> [a]
take Int
n ([Maybe b] -> [Maybe b])
-> ((a -> b) -> [Maybe b]) -> (a -> b) -> [Maybe b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> [Maybe b]
forall a b. Listable a => (a -> b) -> [Maybe b]
funToList)