{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE GADTs #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Type.Sum.Lifted -- Copyright : Copyright (C) 2015 Kyle Carter -- License : BSD3 -- -- Maintainer : Kyle Carter -- Stability : experimental -- Portability : RankNTypes -- -- 'FSum' is a type combinators for representing disjoint sums of -- many functors @(fs :: [k -> *])@ at a single index @(a :: k)@. -- As opposed to one-functor-many-indices 'Sum'. -- ----------------------------------------------------------------------------- module Data.Type.Sum.Lifted where import Data.Type.Index import Type.Class.Witness import Type.Family.List data FSum :: [k -> *] -> k -> * where FInL :: !(f a) -> FSum (f :< fs) a FInR :: !(FSum fs a) -> FSum (f :< fs) a -- | There are no possible values of the type @FSum Ø a@. nilFSum :: FSum Ø a -> Void nilFSum = impossible -- | Decompose a non-empty FSum into either its head or its tail. fdecomp :: FSum (f :< fs) a -> Either (f a) (FSum fs a) fdecomp = \case FInL a -> Left a FInR s -> Right s -- | Inject an element into an FSum. finj :: (f ∈ fs) => f a -> FSum fs a finj = injectFSum elemIndex -- | Project an implicit index out of an FSum. fprj :: (f ∈ fs) => FSum fs a -> Maybe (f a) fprj = findex elemIndex -- | Inject an element into an FSum with an explicitly -- specified Index. injectFSum :: Index fs f -> f a -> FSum fs a injectFSum = \case IZ -> FInL IS x -> FInR . injectFSum x -- | Project an explicit index out of an FSum. findex :: Index fs f -> FSum fs a -> Maybe (f a) findex = \case IZ -> \case FInL a -> Just a _ -> Nothing IS x -> \case FInR s -> findex x s _ -> Nothing instance ListC (Functor <$> fs) => Functor (FSum fs) where fmap f = \case FInL a -> FInL $ f <$> a FInR s -> FInR $ f <$> s instance ListC (Foldable <$> fs) => Foldable (FSum fs) where foldMap f = \case FInL a -> foldMap f a FInR s -> foldMap f s instance ( ListC (Functor <$> fs) , ListC (Foldable <$> fs) , ListC (Traversable <$> fs) ) => Traversable (FSum fs) where traverse f = \case FInL a -> FInL <$> traverse f a FInR s -> FInR <$> traverse f s -- | Map over the single element in an FSum -- with a function that can handle any possible -- element, along with the element's index. imapFSum :: (forall f. Index fs f -> f a -> f b) -> FSum fs a -> FSum fs b imapFSum f = \case FInL a -> FInL $ f IZ a FInR s -> FInR $ imapFSum (f . IS) s -- | Fun fact: Since there is exactly one element in -- an FSum, we don't need the @Monoid@ instance! ifoldMapFSum :: (forall f. Index fs f -> f a -> m) -> FSum fs a -> m ifoldMapFSum f = \case FInL a -> f IZ a FInR s -> ifoldMapFSum (f . IS) s -- | Another fun fact: Since there is exactly one element in -- an FSum, we require only a @Functor@ instance on @g@, rather -- than @Applicative@. itraverseFSum :: Functor g => (forall f. Index fs f -> f a -> g (f b)) -> FSum fs a -> g (FSum fs b) itraverseFSum f = \case FInL a -> FInL <$> f IZ a FInR s -> FInR <$> itraverseFSum (f . IS) s