{-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -Werror=incomplete-patterns #-} {-| Module : Fcf.Alg.Sort Description : Type-level sorting algorithms and utilities Copyright : (c) gspia 2020- License : BSD Maintainer : gspia = Fcf.Alg.Sort -} -------------------------------------------------------------------------------- module Fcf.Alg.Sort where import qualified GHC.TypeLits as TL import Fcf ( If, Eval, Exp, type (<=<), type (=<<) , Flip, Not, TyEq, Pure ) import Fcf.Data.List ( ZipWith, Filter, type (++) ) import Fcf.Data.Nat (Nat) import Fcf.Data.Symbol (Symbol) -------------------------------------------------------------------------------- import Fcf.Alg.Tree (BTreeF(..)) import Fcf.Alg.Morphism import Fcf.Alg.Symbol (SymbolOrd) -------------------------------------------------------------------------------- -- For the doctests: -- $setup -- >>> import qualified Fcf.Data.Nat as N ( type (<) ) -- >>> import qualified Fcf.Alg.Symbol as S ( type (<) ) -------------------------------------------------------------------------------- -- helper for the ListCmp data ListCmpFnd :: [Ordering] -> Exp Ordering type instance Eval (ListCmpFnd '[]) = 'EQ type instance Eval (ListCmpFnd (a ': as)) = Eval (If (Eval (TyEq a 'EQ)) (ListCmpFnd as) (Pure a) ) -- | Compare lists with the given comparison for the elements. data ListCmp :: (a -> a -> Exp Ordering) -> [a] -> [a] -> Exp Ordering type instance Eval (ListCmp f as bs) = Eval (ListCmpFnd =<< ZipWith f as bs) -- | Give true if the first list is before the second, given the comparison -- function for the elements. data ListOrd :: (a -> a -> Exp Ordering) -> [a] -> [a] -> Exp Bool type instance Eval (ListOrd f as bs) = Eval (If (Eval (TyEq 'LT (Eval (ListCmp f as bs)))) (Pure 'True) (Pure 'False) ) -- | Comparison for the Nats. -- -- TODO: Would this fit to Fcf.Data.Nat on first-class-families? data NatOrd :: Nat -> Nat -> Exp Ordering type instance Eval (NatOrd a b) = TL.CmpNat a b -- | Comparison for Symbol lists. -- -- Useful when sorting with Qsort. data SymbolListOrd :: [Symbol] -> [Symbol] -> Exp Bool type instance Eval (SymbolListOrd as bs) = Eval (ListOrd SymbolOrd as bs) -- | Comparison for Nat lists. -- -- Useful when sorting with Qsort. data NatListOrd :: [Nat] -> [Nat] -> Exp Bool type instance Eval (NatListOrd as bs) = Eval (ListOrd NatOrd as bs) -------------------------------------------------------------------------------- -- helper data PartHlp :: (a -> a -> Exp Bool) -> CoAlgebra (BTreeF a) [a] type instance Eval (PartHlp _ '[]) = 'BEmptyF type instance Eval (PartHlp smaller (h ': t)) = 'BNodeF h (Eval (Filter (smaller h) t)) (Eval (Filter (Not <=< smaller h) t)) -- helper data Inord :: Algebra (BTreeF a) [a] type instance Eval (Inord 'BEmptyF) = '[] type instance Eval (Inord ('BNodeF v l r)) = Eval (l ++ (Eval ('[v] ++ r))) -- | Qsort - give the comparison function @a -> a -> Exp Bool@ comparing your -- list elements and then Qsort will order the list. -- -- __Example__ -- -- >>> :kind! Eval (Qsort (N.<) '[5,3,1,9,4,6,3]) -- Eval (Qsort (N.<) '[5,3,1,9,4,6,3]) :: [Nat] -- = '[1, 3, 3, 4, 5, 6, 9] -- -- >>> :kind! Eval (Qsort (S.<) '[ "bb", "e", "a", "e", "d" ]) -- Eval (Qsort (S.<) '[ "bb", "e", "a", "e", "d" ]) :: [Symbol] -- = '["a", "bb", "d", "e", "e"] data Qsort :: (a -> a -> Exp Bool) -> [a] -> Exp [a] type instance Eval (Qsort cmp lst) = Eval (Hylo Inord (PartCmp cmp) lst) -- Helper -- -- We use the Flip version so that using <-comparison will give an inreasing -- Nat-list. Sorting would work without PartCmp. data PartCmp :: (a -> a -> Exp Bool) -> CoAlgebra (BTreeF a) [a] type instance Eval (PartCmp cmp coalg) = Eval (PartHlp (Flip cmp) coalg)