{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE GADTs #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Type.Product -- Copyright : Copyright (C) 2015 Kyle Carter -- License : BSD3 -- -- Maintainer : Kyle Carter -- Stability : experimental -- Portability : RankNTypes -- -- Type combinators for type-level lists, -- lifting @(f :: k -> *)@ to @(Prod f :: [k] -> *)@, -- as well as its constructions, manipulations, and -- eliminations. -- -- 'Prod' is similar in nature to a few others in the Haskell ecosystem, such as: -- -- Oleg Kiselyov's 'HList', from , and -- -- Kenneth Foner's 'ConicList', from . -- ----------------------------------------------------------------------------- module Data.Type.Product where import Data.Type.Boolean import Data.Type.Combinator import Data.Type.Conjunction import Data.Type.Index import Data.Type.Length -- import Data.Type.Quantifier import Type.Class.Higher import Type.Class.Known import Type.Class.Witness import Type.Family.Constraint import Type.Family.List data Prod (f :: k -> *) :: [k] -> * where Ø :: Prod f Ø (:<) :: !(f a) -> !(Prod f as) -> Prod f (a :< as) infixr 5 :< deriving instance ListC (Eq <$> f <$> as) => Eq (Prod f as) deriving instance ( ListC (Eq <$> f <$> as) , ListC (Ord <$> f <$> as) ) => Ord (Prod f as) deriving instance ListC (Show <$> f <$> as) => Show (Prod f as) instance Eq1 f => Eq1 (Prod f) where eq1 = \case  Ø -> \case Ø -> True a :< as -> \case b :< bs -> a =#= b && as =#= bs instance Ord1 f => Ord1 (Prod f) where compare1 = \case Ø -> \case Ø -> EQ a :< as -> \case b :< bs -> compare1 a b `mappend` compare1 as bs instance Show1 f => Show1 (Prod f) where showsPrec1 d = \case Ø -> showString "Ø" a :< as -> showParen (d > 5) $ showsPrec1 6 a . showString " :< " . showsPrec1 6 as instance Read1 f => Read1 (Prod f) where readsPrec1 d s0 = [ (Some Ø,s1) | ("Ø",s1) <- lex s0 ] ++ readParen (d > 5) ( \s1 -> [ (x >>- \a -> xs >>- \as -> Some $ a :< as,s4) | (x,s2) <- readsPrec1 6 s1 , (":<",s3) <- lex s2 , (xs,s4) <- readsPrec1 5 s3 ] ) s0 instance BoolEquality f => BoolEquality (Prod f) where boolEquality = \case Ø -> \case Ø -> True_ (:<){} -> False_ a :< as -> \case b :< bs -> case a .== b of True_ -> case as .== bs of True_ -> True_ False_ -> False_ False_ -> False_ Ø -> False_ instance TestEquality f => TestEquality (Prod f) where testEquality = \case Ø -> \case Ø -> qed _ -> Nothing a :< as -> \case b :< bs -> a =?= b //? as =?= bs //? qed _ -> Nothing -- | Construct a two element Prod. -- Since the precedence of (:>) is higher than (:<), -- we can conveniently write lists like: -- -- >>> a :< b :> c -- -- Which is identical to: -- -- >>> a :< b :< c :< Ø -- pattern (:>) :: (f :: k -> *) (a :: k) -> f (b :: k) -> Prod f '[a,b] pattern a :> b = a :< b :< Ø infix 6 :> -- | Build a singleton Prod. only :: f a -> Prod f '[a] only = (:< Ø) -- | snoc function. insert an element at the end of the list. (>:) :: Prod f as -> f a -> Prod f (as >: a) (>:) = \case Ø -> only b :< as -> (b :<) . (as >:) infixl 6 >: head' :: Prod f (a :< as) -> f a head' (a :< _) = a tail' :: Prod f (a :< as) -> Prod f as tail' (_ :< as) = as -- | Get all but the last element of a non-empty Prod. init' :: Prod f (a :< as) -> Prod f (Init' a as) init' (a :< as) = case as of Ø -> Ø (:<){} -> a :< init' as -- | Get the last element of a non-empty Prod. last' :: Prod f (a :< as) -> f (Last' a as) last' (a :< as) = case as of Ø -> a (:<){} -> last' as reverse' :: Prod f as -> Prod f (Reverse as) reverse' = \case Ø -> Ø a :< as -> reverse' as >: a append' :: Prod f as -> Prod f bs -> Prod f (as ++ bs) append' = \case Ø -> id a :< as -> (a :<) . append' as {- lookup' :: TestEquality f => f a -> Prod (f :&: g) as -> Maybe (g a) lookup' a = \case Ø -> Nothing (b :&: v) :< bs -> witMaybe (a =?= b) (Just v) $ lookup' a bs -} lookupPar :: TestEquality f => f a -> Prod (f :*: g) as -> Maybe (Some g) lookupPar a = \case Ø -> Nothing (b :*: v) :< bs -> witMaybe (a =?= b) (Just $ Some v) $ lookupPar a bs permute :: Known Length bs => (forall x. Index bs x -> Index as x) -> Prod f as -> Prod f bs permute f as = permute' f as known permute' :: (forall x. Index bs x -> Index as x) -> Prod f as -> Length bs -> Prod f bs permute' f as = \case LZ -> Ø LS l -> index (f IZ) as :< permute' (f . IS) as l -- Tuple {{{ -- | A Prod of simple Haskell types. type Tuple = Prod I -- | Singleton Tuple. only_ :: a -> Tuple '[a] only_ = only . I -- | Cons onto a Tuple. pattern (::<) :: a -> Tuple as -> Tuple (a :< as) pattern a ::< as = I a :< as infixr 5 ::< -- | Snoc onto a Tuple. (>::) :: Tuple as -> a -> Tuple (as >: a) (>::) = \case Ø -> only_ b :< as -> (b :<) . (as >::) infixl 6 >:: -- }}} elimProd :: p Ø -> (forall x xs. Index as x -> f x -> p xs -> p (x :< xs)) -> Prod f as -> p as elimProd n c = \case Ø -> n a :< as -> c IZ a $ elimProd n (c . IS) as onHead' :: (f a -> f b) -> Prod f (a :< as) -> Prod f (b :< as) onHead' f (a :< as) = f a :< as onTail' :: (Prod f as -> Prod f bs) -> Prod f (a :< as) -> Prod f (a :< bs) onTail' f (a :< as) = a :< f as uncurry' :: (f a -> Prod f as -> r) -> Prod f (a :< as) -> r uncurry' f (a :< as) = f a as curry' :: (l ~ (a :< as)) => (Prod f l -> r) -> f a -> Prod f as -> r curry' f a as = f $ a :< as index :: Index as a -> Prod f as -> f a index = \case IZ -> head' IS x -> index x . tail' select :: Prod (Index as) bs -> Prod f as -> Prod f bs select = \case Ø -> pure Ø x: (:<) <$> index x <*> select xs indices :: forall as. Known Length as => Prod (Index as) as indices = indices' known indices' :: Length as -> Prod (Index as) as indices' = \case LZ -> Ø LS l -> IZ :< map1 IS (indices' l) instance Functor1 Prod where map1 f = \case Ø -> Ø a :< as -> f a :< map1 f as instance IxFunctor1 Index Prod where imap1 f = \case Ø -> Ø a :< as -> f IZ a :< imap1 (f . IS) as instance Foldable1 Prod where foldMap1 f = \case Ø -> mempty a :< as -> f a `mappend` foldMap1 f as instance IxFoldable1 Index Prod where ifoldMap1 f = \case Ø -> mempty a :< as -> f IZ a `mappend` ifoldMap1 (f . IS) as instance Traversable1 Prod where traverse1 f = \case Ø -> pure Ø a :< as -> (:<) <$> f a <*> traverse1 f as instance IxTraversable1 Index Prod where itraverse1 f = \case Ø -> pure Ø a :< as -> (:<) <$> f IZ a <*> itraverse1 (f . IS) as instance (Known Length as, Every (Known f) as) => Known (Prod f) as where type KnownC (Prod f) as = (Known Length as, Every (Known f) as) known = go known where go :: Every (Known f) xs => Length xs -> Prod f xs go = \case LZ -> Ø LS l -> known :< go l {- instance Known (Prod f) Ø where known = Ø instance (Known f a, Known (Prod f) as) => Known (Prod f) (a :< as) where type KnownC (Prod f) (a :< as) = (Known f a, Known (Prod f) as) known = known :< known -} type family Witnesses (ps :: [Constraint]) (qs :: [Constraint]) (f :: k -> *) (as :: [k]) :: Constraint where Witnesses Ø Ø f Ø = ØC Witnesses (p :< ps) (q :< qs) f (a :< as) = (Witness p q (f a), Witnesses ps qs f as) instance Witness ØC ØC (Prod f Ø) where r \\ _ = r instance (Witness p q (f a), Witness s t (Prod f as)) => Witness (p,s) (q,t) (Prod f (a :< as)) where type WitnessC (p,s) (q,t) (Prod f (a :< as)) = (Witness p q (f a), Witness s t (Prod f as)) r \\ (a :< as) = r \\ a \\ as toList :: (forall a. f a -> r) -> Prod f as -> [r] toList f = \case Ø -> [] a :< as -> f a : toList f as