{-# LANGUAGE TypeFamilies #-}

{-|
Copyright   : Predictable Network Solutions Ltd., 2020-2024
License     : BSD-3-Clause
Description : Type class for functions, e.g. polynomials.
-}
module Data.Function.Class
    ( Function (..)
    ) where

import qualified Data.Map as Map
import qualified Data.Set as Set

-- | An instance of 'Function' is a type that represents functions.
-- Function can be evaluated at points in their 'Domain'.
--
-- Examples: Polynomials, trigonometric polynomials, piecewise polynomials, …
class Function f where
    -- | The __domain__ of definition of the function.
    type Domain f
    -- | The __codomain__ of a function is the set of potential function values,
    -- i.e. function values never lie outside this set.
    --
    -- In contrast, the set of actual function values
    -- is called the __image__ and
    -- is typically a strict subset of the codomain.
    type Codomain f

    -- | Evaluate a function at a point in its 'Domain'.
    eval :: f -> Domain f -> Codomain f

-- | Functions are 'Function'.
instance Function (a -> b) where
    type Domain (a -> b) = a
    type Codomain (a -> b) = b

    eval :: (a -> b) -> Domain (a -> b) -> Codomain (a -> b)
eval = (a -> b) -> a -> b
(a -> b) -> Domain (a -> b) -> Codomain (a -> b)
forall a. a -> a
id

-- | @'Map.Map' k v@ represents a function @k -> Maybe v@.
--
-- > Domain   (Map k v) = k
-- > Codomain (Map k v) = Maybe v
instance Ord k => Function (Map.Map k v) where
    type instance Domain (Map.Map k v) = k
    type instance Codomain (Map.Map k v) = Maybe v

    eval :: Map k v -> Domain (Map k v) -> Codomain (Map k v)
eval = (k -> Map k v -> Maybe v) -> Map k v -> k -> Maybe v
forall a b c. (a -> b -> c) -> b -> a -> c
flip k -> Map k v -> Maybe v
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup

-- | @'Set.Set' v@ represents a function @v -> Bool@.
--
-- > Domain   (Set v) = v
-- > Codomain (Set v) = Bool
instance Ord v => Function (Set.Set v) where
    type Domain (Set.Set v) = v
    type Codomain (Set.Set v) = Bool

    eval :: Set v -> Domain (Set v) -> Codomain (Set v)
eval = (v -> Set v -> Bool) -> Set v -> v -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip v -> Set v -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member