{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE UndecidableInstances #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Extensible.Sum -- Copyright : (c) Fumiaki Kinoshita 2018 -- License : BSD3 -- -- Maintainer : Fumiaki Kinoshita -- ------------------------------------------------------------------------ module Data.Extensible.Sum ( (:/)(..) , hoist , embed , strike , strikeAt , (<:|) , exhaust , embedAssoc ) where import Data.Extensible.Class import Data.Kind (Type) import Data.Profunctor import Data.Proxy import Data.Type.Equality import Type.Membership -- | The extensible sum type -- -- @(:/) :: [k] -> (k -> Type) -> Type@ -- data (xs :: [k]) :/ (h :: k -> Type) where EmbedAt :: !(Membership xs x) -> h x -> xs :/ h instance Enum (xs :/ Proxy) where fromEnum (EmbedAt m _) = fromIntegral $ getMemberId m toEnum i = reifyMembership (fromIntegral i) $ \m -> EmbedAt m Proxy instance (Last xs ∈ xs) => Bounded (xs :/ Proxy) where minBound = reifyMembership 0 $ \m -> EmbedAt m Proxy maxBound = EmbedAt (membership :: Membership xs (Last xs)) Proxy -- | Change the wrapper. hoist :: (forall x. g x -> h x) -> xs :/ g -> xs :/ h hoist f (EmbedAt p h) = EmbedAt p (f h) {-# INLINE hoist #-} -- | /O(1)/ lift a value. embed :: (x ∈ xs) => h x -> xs :/ h embed = EmbedAt membership {-# INLINE embed #-} -- | Try to extract something you want. strike :: forall h x xs. (x ∈ xs) => xs :/ h -> Maybe (h x) strike = strikeAt membership {-# INLINE strike #-} -- | Try to extract something you want. strikeAt :: forall h x xs. Membership xs x -> xs :/ h -> Maybe (h x) strikeAt q (EmbedAt p h) = case compareMembership p q of Right Refl -> Just h _ -> Nothing {-# INLINE strikeAt #-} -- | /O(1)/ Naive pattern match (<:|) :: (h x -> r) -> (xs :/ h -> r) -> (x ': xs) :/ h -> r (<:|) r c = \(EmbedAt i h) -> testMembership i (\Refl -> r h) (\j -> c (EmbedAt j h)) infixr 1 <:| {-# INLINE (<:|) #-} -- | There is no empty union. exhaust :: '[] :/ h -> r exhaust _ = error "Impossible" -- | Embed a value, but focuses on its key. embedAssoc :: Lookup xs k a => h (k ':> a) -> xs :/ h embedAssoc = EmbedAt association {-# INLINE embedAssoc #-} instance (Applicative f, Choice p) => Extensible f p (:/) where pieceAt m = dimap (\t@(EmbedAt i h) -> case compareMembership i m of Right Refl -> Right h Left _ -> Left t) (either pure (fmap (EmbedAt m))) . right' {-# INLINABLE pieceAt #-}