union-map-0.0: src/Data/OpenUnion/Internal.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.OpenUnion.Internal where
import Control.Applicative
import Data.Monoid hiding (All)
import GHC.Exts (Constraint)
data Union (as :: [*]) where
Single :: a -> Union (a ': as)
Union :: Union as -> Union (a ': as)
Exhausted :: Union '[] -> Union '[]
exhaust :: Union '[] -> a
exhaust (Exhausted x) = exhaust x
{-# INLINE exhaust #-}
type family All (ca :: * -> Constraint) (as :: [*]) :: Constraint
type instance All ca '[] = ()
type instance All ca (a ': as) = (ca a, All ca as)
instance (All Show r) => Show (Union r) where
show (Single x) = show x
show (Union u) = show u
show _ = undefined
(||>) :: (a -> r) -> (Union as -> r) -> (Union (a ': as) -> r)
(||>) f _ (Single x) = f x
(||>) _ g (Union u) = g u
infixr 2 ||>
{-# INLINE (||>) #-}
data Position (a :: *) (as :: [*]) where
Zero :: Position a (a ': as)
Succ :: Position a as -> Position a (b ': as)
class Member a as | as -> a where
position :: Position a as
instance Member a (a ': as) where
position = Zero
instance Member a as => Member a (b ': as) where
position = Succ position
-- | Lift a value into a Union.
liftU :: forall a as. Member a as => a -> Union as
liftU x = go (position :: Position a as)
where
go :: Position a bs -> Union bs
go Zero = Single x
go (Succ n) = Union $ go n
{-# INLINE liftU #-}
-- | Traversal for a specific element.
picked :: forall a as f. (Applicative f, Member a as) => (a -> f a) -> Union as -> f (Union as)
picked k = go (position :: Position a as)
where
go :: Position a bs -> Union bs -> f (Union bs)
go Zero (Single x) = Single <$> k x
go Zero u@(Union _) = pure u
go (Succ _) u@(Single _) = pure u
go (Succ n) (Union u) = Union <$> go n u
{-# INLINE picked #-}
-- | Retrieve the value from a Union.
retractU :: Member a as => Union as -> Maybe a
retractU = getFirst . getConst . picked (Const . First . Just)
{-# INLINE retractU #-}
-- | Lift a function into @Union@.
hoistU :: Member a as => (a -> a) -> Union as -> Union as
hoistU f = getId . picked (Id . f)
{-# INLINE hoistU #-}
-- | Instead of Identity.
newtype Id a = Id { getId :: a }
instance Functor Id where
fmap f (Id a) = Id (f a)
instance Applicative Id where
pure = Id
Id f <*> Id a = Id (f a)
class Include as bs where
reunion :: Union as -> Union bs
instance (Member a bs, Include as bs) => Include (a ': as) bs where
reunion (Single x) = liftU x
reunion (Union u) = reunion u
instance (Include '[] bs) where
reunion = exhaust
type a ∈ as = Member a as
type as ⊆ bs = Include as bs
type as = bs = (Include as bs, Include bs as)
infix 4 ∈
infix 4 ⊆
infix 4 =