{-|

Module      : FuncType
Description : Types with functions as values.
Copyright   : © Frank Jung, 2023
License     : GPL-3.0-only

== Defining data with functions

Code based off [Haskell Cookbook by Yogesh Sajanikar, Chapter 3, Defining
data with functions, page
83.](https://www.packtpub.com/product/haskell-cookbook/9781786461353)

Some use-cases for using functions as types:

== Generic programming and type classes

Functions as types are essential in generic programming, where algorithms are
designed to operate seamlessly across various data types. Type classes, which
organize types according to shared characteristics, frequently leverage
functions as types to articulate these characteristics and facilitate generic
operations.

==  Meta-programming and type-level computations

Meta-programming and type-level computations involve the use of functions as
types. This concept allows programs to manipulate other programs or their
representations. Additionally, functions as types facilitate type-level
computations, where calculations are carried out during compile time using type
information.

-}

module FuncType
  ( -- * Types
    Func (..)
    -- * Functions
  , apply
  , compose
  ) where

-- | Type with a function value.
newtype Func a b = Func (a -> b)

-- | Apply function to a value.
apply :: Func a b -> a -> b
apply :: forall a b. Func a b -> a -> b
apply (Func a -> b
f) = a -> b
f

-- | Compose two functions.
compose :: Func a b -> Func b c -> Func a c
compose :: forall a b c. Func a b -> Func b c -> Func a c
compose (Func a -> b
f) (Func b -> c
g) = (a -> c) -> Func a c
forall a b. (a -> b) -> Func a b
Func (b -> c
g (b -> c) -> (a -> b) -> a -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
f)