packages feed

fir-0.1.0.0: src/Data/Type/POrd.hs

{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE PolyKinds            #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE TypeOperators        #-}
{-# LANGUAGE UndecidableInstances #-}

{-|
Module: Data.Type.POrd

Promotion of the 'Ord' type class.
-}

module Data.Type.POrd where

-- base
import Data.Type.Bool
  ( If, Not )
import GHC.TypeLits
  ( Symbol, CmpSymbol )
import GHC.TypeNats
  ( Nat, CmpNat, type (+), type (-) )

-- fir
import Data.Type.LazyEquality
  ( LazyEq )

-------------------------------------------------------------------------------

infix 4 :<
infix 4 :>
infix 4 :<=
infix 4 :>=
infix 4 `Compare`

class POrd a where
  type Compare (x :: a) (y :: a) :: Ordering
  type (x :: a) :<  (y :: a) :: Bool
  type x :< y = Compare x y `LazyEq` LT
  type (x :: a) :>  (y :: a) :: Bool
  type x :> y = Compare x y `LazyEq` GT
  type (x :: a) :<= (y :: a) :: Bool
  type x :<= y = Not (Compare x y `LazyEq` GT)
  type (x :: a) :>= (y :: a) :: Bool
  type x :>= y = Not (Compare x y `LazyEq` LT)
  type Max (x :: a) (y :: a) :: a
  type Max x y = If (x :< y) y x
  type Min (x :: a) (y :: a) :: a
  type Min x y = If (x :> y) y x

instance POrd Symbol where
  type Compare x y = CmpSymbol x y

instance POrd Nat where
  type Compare x y = CmpNat x y
  type Max     x y = MaxNat x y

-- | The maximum of two natural numbers.
--
-- The equations for @0@ hold off reduction until both arguments
-- have been evaluated, which avoids duplicating unevaluated arguments.
type family MaxNat (x :: Nat) (y :: Nat) :: Nat where
  MaxNat 0 y = y
  MaxNat x 0 = x
  MaxNat x y = If (x :< y) y x

instance ( POrd a, POrd b ) => POrd (a,b) where
  type Compare p1 p2 = Lexicographic2 p1 p2

type family Lexicographic2 (p1 :: (a,b)) (p2 :: (a,b)) :: Ordering where
  Lexicographic2 '(x ,y1) '(x ,y2) = Compare y1 y2
  Lexicographic2 '(x1,_)  '(x2,_)  = Compare x1 x2

instance ( POrd a, POrd b, POrd c ) => POrd (a,b,c) where
  type Compare t1 t2 = Lexicographic3 t1 t2

type family Lexicographic3 (t1 :: (a,b,c)) (t2 :: (a,b,c)) :: Ordering where
  Lexicographic3 '(x ,y ,z1) '(x ,y ,z2) = Compare z1 z2
  Lexicographic3 '(x ,y1,_ ) '(x ,y2,_ ) = Compare y1 y2
  Lexicographic3 '(x1,_ ,_ ) '(x2,_ ,_ ) = Compare x1 x2


class POrd a => PEnum a where
  type Succ (x :: a) :: a
  type Pred (x :: a) :: a
  type ToEnum (i :: Nat) :: a
  type FromEnum (x :: a) :: Nat
  type EnumFromTo (x :: a) (y :: a) :: [a]
  type EnumFromTo x y
    = EnumFromToDefault x y (x :> y)
  type EnumFromOfCount (x :: a) (i :: Nat) :: [a]
  type EnumFromOfCount x i
    = EnumFromOfCountDefault x i

type family EnumFromToDefault (x :: a) (y :: a) (x_gt_y :: Bool) :: [a] where
  EnumFromToDefault _ _ 'True  = '[]
  EnumFromToDefault x y 'False = x ': EnumFromTo (Succ x) y

type family EnumFromOfCountDefault (x :: a) (i :: Nat) :: [a] where
  EnumFromOfCountDefault _ 0 = '[]
  EnumFromOfCountDefault x i = x ': EnumFromOfCountDefault (Succ x) (Pred i)

instance PEnum Nat where
  type Succ x     = x + 1
  type Pred x     = x - 1
  type ToEnum x   = x
  type FromEnum x = x