row-types-0.3.0.0: Data/Row/Internal.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE UndecidableSuperClasses #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.Row.Internal
--
-- This module implements the internals of open records and variants.
--
-----------------------------------------------------------------------------
module Data.Row.Internal
(
-- * Rows
Row(..)
, Label(..)
, KnownSymbol
, LT(..)
, Empty
, HideType(..)
-- * Row Operations
, Extend, Modify, Rename
, type (.==), type (.!), type (.-), type (.\\)
-- $merges
, type (.+), type (.\/), type (.//)
-- * Row Constraints
, Lacks, type (.\), HasType
, Forall(..)
, BiForall(..)
, BiConstraint
, Unconstrained1
, Unconstrained2
, WellBehaved, AllUniqueLabels
, Ap, Zip, Map, Subset, Disjoint
-- * Helper functions
, Labels, labels, labels'
, show'
, toKey
, type (≈)
, mapForall
, freeForall
, uniqueMap
, mapHas
, IsA(..)
, As(..)
)
where
import Data.Constraint
import Data.Functor.Const
import Data.Proxy
import Data.String (IsString (fromString))
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Type.Equality (type (==))
import qualified Unsafe.Coerce as UNSAFE
import GHC.OverloadedLabels
import GHC.TypeLits
import qualified GHC.TypeLits as TL
{--------------------------------------------------------------------
Rows
--------------------------------------------------------------------}
-- | The kind of rows. This type is only used as a datakind. A row is a typelevel entity telling us
-- which symbols are associated with which types.
newtype Row a = R [LT a]
-- ^ A row is a list of symbol-to-type pairs that should always be sorted
-- lexically by the symbol.
-- The constructor is exported here (because this is an internal module) but
-- should not be exported elsewhere.
-- | The kind of elements of rows. Each element is a label and its associated type.
data LT a = Symbol :-> a
-- | A label
data Label (s :: Symbol) = Label
deriving (Eq)
instance KnownSymbol s => Show (Label s) where
show = symbolVal
instance x ≈ y => IsLabel x (Label y) where
#if __GLASGOW_HASKELL__ >= 802
fromLabel = Label
#else
fromLabel _ = Label
#endif
-- | A helper function for showing labels
show' :: (IsString s, Show a) => a -> s
show' = fromString . show
-- | A helper function to turn a Label directly into 'Text'.
toKey :: forall s. KnownSymbol s => Label s -> Text
toKey = Text.pack . symbolVal
-- | Type level version of 'empty'
type Empty = R '[]
-- | Elements stored in a Row type are usually hidden.
data HideType where
HideType :: a -> HideType
{--------------------------------------------------------------------
Row operations
--------------------------------------------------------------------}
infixl 4 .\ {- This comment needed to appease CPP -}
-- | Does the row lack (i.e. it does not have) the specified label?
type family (r :: Row k) .\ (l :: Symbol) :: Constraint where
R r .\ l = LacksR l r r
-- | Type level Row extension
type family Extend (l :: Symbol) (a :: k) (r :: Row k) :: Row k where
Extend l a (R x) = R (Inject (l :-> a) x)
-- | Type level Row modification
type family Modify (l :: Symbol) (a :: k) (r :: Row k) :: Row k where
Modify l a (R ρ) = R (ModifyR l a ρ)
-- | Type level row renaming
type family Rename (l :: Symbol) (l' :: Symbol) (r :: Row k) :: Row k where
Rename l l' r = Extend l' (r .! l) (r .- l)
infixl 5 .!
-- | Type level label fetching
type family (r :: Row k) .! (t :: Symbol) :: k where
R r .! l = Get l r
infixl 6 .-
-- | Type level Row element removal
type family (r :: Row k) .- (s :: Symbol) :: Row k where
R r .- l = R (Remove l r)
infixl 6 .\\ {- This comment needed to appease CPP -}
-- | Type level Row difference. That is, @l '.\\' r@ is the row remaining after
-- removing any matching elements of @r@ from @l@.
type family (l :: Row k) .\\ (r :: Row k) :: Row k where
R l .\\ R r = R (Diff l r)
-- $merges
-- == Various row-type merges
-- The difference between '.+' (read "append"), '.\/' (read "min-join"), and
-- '.\\' (read "const-union") comes down to how duplicates are handled.
-- In '.+', the two given row-types must be entirely unique. Even the same
-- entry in both row-types is forbidden. In '.\/', this final restriction is
-- relaxed, allowing two row-types that have no conflicts to be merged in the
-- logical way. The '.\\' operator is the most liberal, allowing any two row-types
-- to be merged together, and whenever there is a conflict, favoring the left argument.
--
-- As examples of use:
--
-- - '.+' is used when appending two records, assuring that those two records are
-- entirely disjoint.
--
-- - '.\/' is used when diversifying a variant, allowing some extension to the
-- row-type so long as no original types have changed.
--
-- - './/' is used when doing record overwrite, allowing data in a record to
-- totally overwrite what was previously there.
infixl 6 .+
-- | Type level Row append
type family (l :: Row k) .+ (r :: Row k) :: Row k where
R l .+ R r = R (Merge l r)
infixl 6 .\/
-- | The minimum join of the two rows.
type family (l :: Row k) .\/ (r :: Row k) where
R l .\/ R r = R (MinJoinR l r)
infixl 6 .//
-- | The overwriting union, where the left row overwrites the types of the right
-- row where the labels overlap.
type family (l :: Row k) .// (r :: Row k) where
R l .// R r = R (ConstUnionR l r)
{--------------------------------------------------------------------
Syntactic sugar for record operations
--------------------------------------------------------------------}
-- | Alias for '.\'. It is a class rather than an alias, so that
-- it can be partially applied.
class Lacks (l :: Symbol) (r :: Row *)
instance (r .\ l) => Lacks l r
-- | Alias for @(r .! l) ≈ a@. It is a class rather than an alias, so that
-- it can be partially applied.
class (r .! l ≈ a) => HasType l a r
instance (r .! l ≈ a) => HasType l a r
-- | A type level way to create a singleton Row.
infix 7 .==
type (l :: Symbol) .== (a :: k) = Extend l a Empty
{--------------------------------------------------------------------
Constrained record operations
--------------------------------------------------------------------}
-- | Any structure over a row in which every element is similarly constrained can
-- be metamorphized into another structure over the same row.
class Forall (r :: Row k) (c :: k -> Constraint) where
-- | A metamorphism is an unfold followed by a fold. This one is for
-- product-like row-types (e.g. Rec).
metamorph :: forall (f :: Row k -> *) (g :: Row k -> *) (h :: k -> *).
Proxy h
-> (f Empty -> g Empty)
-- ^ The way to transform the empty element
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> (h τ, f ('R ρ)))
-- ^ The unfold
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> h τ -> g ('R ρ) -> g ('R (ℓ :-> τ ': ρ)))
-- ^ The fold
-> f r -- ^ The input structure
-> g r
-- | A metamorphism is an unfold followed by a fold. This one is for
-- sum-like row-types (e.g. Var).
metamorph' :: forall (f :: Row k -> *) (g :: Row k -> *) (h :: k -> *).
Proxy h
-> (f Empty -> g Empty)
-- ^ The way to transform the empty element
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> Either (h τ) (f ('R ρ)))
-- ^ The unfold
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> Either (h τ) (g ('R ρ)) -> g ('R (ℓ :-> τ ': ρ)))
-- ^ The fold
-> f r -- ^ The input structure
-> g r
-- | This data type is used to for its ability to existentially bind a type
-- variable. Particularly, it says that for the type 'a', there exists a 't'
-- such that 'a ~ f t' and 'c t' holds.
data As c f a where
As :: forall c f a t. (a ~ f t, c t) => As c f a
-- | A class to capture the idea of 'As' so that it can be partially applied in
-- a context.
class IsA c f a where
as :: As c f a
instance c a => IsA c f (f a) where
as = As
-- | An internal type used by the 'metamorph' in 'mapForall'.
newtype MapForall c f (r :: Row k) = MapForall { unMapForall :: Dict (Forall (Map f r) (IsA c f)) }
-- | This allows us to derive a `Forall (Map f r) ..` from a `Forall r ..`.
mapForall :: forall f c ρ. Forall ρ c :- Forall (Map f ρ) (IsA c f)
mapForall = Sub $ unMapForall $ metamorph @_ @ρ @c @(Const ()) @(MapForall c f) @(Const ()) Proxy empty uncons cons $ Const ()
where empty :: Const () Empty -> MapForall c f Empty
empty _ = MapForall Dict
uncons :: forall l t r. (KnownSymbol l, c t)
=> Label l -> Const () ('R (l :-> t ': r)) -> (Const () t, Const () ('R r))
uncons _ _ = (Const (), Const ())
cons :: forall ℓ τ ρ. (KnownSymbol ℓ, c τ)
=> Label ℓ -> Const () τ -> MapForall c f ('R ρ)
-> MapForall c f ('R (ℓ :-> τ ': ρ))
cons _ _ (MapForall Dict) = MapForall Dict
-- | Map preserves uniqueness of labels.
uniqueMap :: forall f ρ. AllUniqueLabels ρ :- AllUniqueLabels (Map f ρ)
uniqueMap = Sub $ UNSAFE.unsafeCoerce @(Dict Unconstrained) Dict
-- | Allow any 'Forall` over a row-type, be usable for 'Unconstrained1'.
freeForall :: forall r c. Forall r c :- Forall r Unconstrained1
freeForall = Sub $ UNSAFE.unsafeCoerce @(Dict (Forall r c)) Dict
-- | This allows us to derive `Map f r .! l ≈ f t` from `r .! l ≈ t`
mapHas :: forall f r l t. (r .! l ≈ t) :- (Map f r .! l ≈ f t)
mapHas = Sub $ UNSAFE.unsafeCoerce $ Dict @(r .! l ≈ t)
instance Forall (R '[]) c where
{-# INLINE metamorph #-}
metamorph _ empty _ _ = empty
{-# INLINE metamorph' #-}
metamorph' _ empty _ _ = empty
instance (KnownSymbol ℓ, c τ, Forall ('R ρ) c) => Forall ('R (ℓ :-> τ ': ρ) :: Row k) c where
metamorph :: forall (f :: Row k -> *) (g :: Row k -> *) (h :: k -> *).
Proxy h
-> (f Empty -> g Empty)
-- ^ The way to transform the empty element
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> (h τ, f ('R ρ)))
-- ^ The unfold
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> h τ -> g ('R ρ) -> g ('R (ℓ :-> τ ': ρ)))
-- ^ The fold
-> f ('R (ℓ :-> τ ': ρ)) -- ^ The input structure
-> g ('R (ℓ :-> τ ': ρ))
{-# INLINE metamorph #-}
metamorph _ empty uncons cons r = cons Label t $ metamorph @_ @('R ρ) @c @_ @_ @h Proxy empty uncons cons r'
where (t, r') = uncons Label r
metamorph' :: forall (f :: Row k -> *) (g :: Row k -> *) (h :: k -> *).
Proxy h
-> (f Empty -> g Empty)
-- ^ The way to transform the empty element
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> Either (h τ) (f ('R ρ)))
-- ^ The unfold
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> Either (h τ) (g ('R ρ)) -> g ('R (ℓ :-> τ ': ρ)))
-- ^ The fold
-> f ('R (ℓ :-> τ ': ρ)) -- ^ The input structure
-> g ('R (ℓ :-> τ ': ρ))
{-# INLINE metamorph' #-}
metamorph' _ empty uncons cons r = cons Label $ metamorph' @_ @('R ρ) @c @_ @_ @h Proxy empty uncons cons <$> uncons Label r
-- | Any structure over two rows in which the elements of each row satisfy some
-- constraints can be metamorphized into another structure over both of the
-- rows.
class BiForall (r1 :: Row k1) (r2 :: Row k2) (c :: k1 -> k2 -> Constraint) where
-- | A metamorphism is a fold followed by an unfold. This one is for
-- product-like row-types.
biMetamorph :: forall (f :: Row k1 -> Row k2 -> *) (g :: Row k1 -> Row k2 -> *)
(h :: k1 -> k2 -> *).
Proxy h
-> (f Empty Empty -> g Empty Empty)
-> (forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1 τ2)
=> Label ℓ
-> f ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2))
-> (h τ1 τ2, f ('R ρ1) ('R ρ2)))
-> (forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1 τ2)
=> Label ℓ -> h τ1 τ2 -> g ('R ρ1) ('R ρ2) -> g ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2)))
-> f r1 r2 -> g r1 r2
-- | A metamorphism is a fold followed by an unfold. This one is for
-- sum-like row-types.
biMetamorph' :: forall (f :: Row k1 -> Row k2 -> *) (g :: Row k1 -> Row k2 -> *)
(h :: k1 -> k2 -> *).
Proxy h
-> (f Empty Empty -> g Empty Empty)
-> (forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1 τ2)
=> Label ℓ
-> f ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2))
-> Either (h τ1 τ2) (f ('R ρ1) ('R ρ2)))
-> (forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1 τ2)
=> Label ℓ -> Either (h τ1 τ2) (g ('R ρ1) ('R ρ2)) -> g ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2)))
-> f r1 r2 -> g r1 r2
instance BiForall (R '[]) (R '[]) c1 where
{-# INLINE biMetamorph #-}
biMetamorph _ empty _ _ = empty
biMetamorph' _ empty _ _ = empty
instance (KnownSymbol ℓ, c τ1 τ2, BiForall ('R ρ1) ('R ρ2) c)
=> BiForall ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2)) c where
{-# INLINE biMetamorph #-}
biMetamorph h empty uncons cons r = cons (Label @ℓ) t $ biMetamorph @_ @_ @('R ρ1) @('R ρ2) @c h empty uncons cons r'
where (t, r') = uncons (Label @ℓ) r
{-# INLINE biMetamorph' #-}
biMetamorph' h empty uncons cons r =
cons (Label @ℓ) $ biMetamorph' @_ @_ @('R ρ1) @('R ρ2) @c h empty uncons cons <$> uncons (Label @ℓ) r
-- | A null constraint
class Unconstrained
instance Unconstrained
-- | A null constraint of one argument
class Unconstrained1 a
instance Unconstrained1 a
-- | A null constraint of two arguments
class Unconstrained2 a b
instance Unconstrained2 a b
-- | A pair of constraints
class (c1 x, c2 y) => BiConstraint c1 c2 x y
instance (c1 x, c2 y) => BiConstraint c1 c2 x y
-- | The labels in a Row.
type family Labels (r :: Row a) where
Labels (R '[]) = '[]
Labels (R (l :-> a ': xs)) = l ': Labels (R xs)
-- | Return a list of the labels in a row type.
labels :: forall ρ c s. (IsString s, Forall ρ c) => [s]
labels = getConst $ metamorph @_ @ρ @c @(Const ()) @(Const [s]) @(Const ()) Proxy (const $ Const []) doUncons doCons (Const ())
where doUncons _ _ = (Const (), Const ())
doCons l _ (Const c) = Const $ show' l : c
-- | Return a list of the labels in a row type and is specialized to the 'Unconstrained1' constraint.
labels' :: forall ρ s. (IsString s, Forall ρ Unconstrained1) => [s]
labels' = labels @ρ @Unconstrained1
{--------------------------------------------------------------------
Convenient type families and classes
--------------------------------------------------------------------}
-- | A convenient way to provide common, easy constraints
type WellBehaved ρ = (Forall ρ Unconstrained1, AllUniqueLabels ρ)
-- | Are all of the labels in this Row unique?
type family AllUniqueLabels (r :: Row k) :: Constraint where
AllUniqueLabels (R r) = AllUniqueLabelsR r
type family AllUniqueLabelsR (r :: [LT k]) :: Constraint where
AllUniqueLabelsR '[] = Unconstrained
AllUniqueLabelsR '[l :-> a] = Unconstrained
AllUniqueLabelsR (l :-> a ': l :-> b ': _) = TypeError
(TL.Text "The label " :<>: ShowType l :<>: TL.Text " is not unique."
:$$: TL.Text "It is assigned to both " :<>: ShowType a :<>: TL.Text " and " :<>: ShowType b)
AllUniqueLabelsR (l :-> a ': l' :-> b ': r) = AllUniqueLabelsR (l' :-> b ': r)
-- | Is the first row a subset of the second?
type family Subset (r1 :: Row k) (r2 :: Row k) :: Constraint where
Subset (R r1) (R r2) = SubsetR r1 r2
type family SubsetR (r1 :: [LT k]) (r2 :: [LT k]) :: Constraint where
SubsetR '[] _ = Unconstrained
SubsetR x '[] = TypeError (TL.Text "One row-type is not a subset of the other."
:$$: TL.Text "The first contains the bindings " :<>: ShowType x
:<>: TL.Text " while the second does not.")
SubsetR (l :-> a ': x) (l :-> a ': y) = SubsetR x y
SubsetR (l :-> a ': x) (l :-> b ': y) =
TypeError (TL.Text "One row-type is not a subset of the other."
:$$: TL.Text "The first assigns the label " :<>: ShowType l :<>: TL.Text " to "
:<>: ShowType a :<>: TL.Text " while the second assigns it to " :<>: ShowType b)
SubsetR (hl :-> al ': tl) (hr :-> ar ': tr) =
Ifte (hl <=.? hr)
(TypeError (TL.Text "One row-type is not a subset of the other."
:$$: TL.Text "The first assigns the label " :<>: ShowType hl :<>: TL.Text " to "
:<>: ShowType al :<>: TL.Text " while the second has no assignment for it."))
(SubsetR (hl :-> al ': tl) tr)
-- | A type synonym for disjointness.
type Disjoint l r = ( WellBehaved l
, WellBehaved r
, Subset l (l .+ r)
, Subset r (l .+ r)
, l .+ r .\\ l ≈ r
, l .+ r .\\ r ≈ l)
-- | Map a type level function over a Row.
type family Map (f :: a -> b) (r :: Row a) :: Row b where
Map f (R r) = R (MapR f r)
type family MapR (f :: a -> b) (r :: [LT a]) :: [LT b] where
MapR f '[] = '[]
MapR f (l :-> v ': t) = l :-> f v ': MapR f t
-- | Take two rows with the same labels, and apply the type operator from the
-- first row to the type of the second.
type family Ap (fs :: Row (a -> b)) (r :: Row a) :: Row b where
Ap (R fs) (R r) = R (ApR fs r)
type family ApR (fs :: [LT (a -> b)]) (r :: [LT a]) :: [LT b] where
ApR '[] '[] = '[]
ApR (l :-> f ': tf) (l :-> v ': tv) = l :-> f v ': ApR tf tv
ApR _ _ = TypeError (TL.Text "Row types with different label sets cannot be App'd together.")
-- | Zips two rows together to create a Row of the pairs.
-- The two rows must have the same set of labels.
type family Zip (r1 :: Row *) (r2 :: Row *) where
Zip (R r1) (R r2) = R (ZipR r1 r2)
type family ZipR (r1 :: [LT *]) (r2 :: [LT *]) where
ZipR '[] '[] = '[]
ZipR (l :-> t1 ': r1) (l :-> t2 ': r2) =
l :-> (t1, t2) ': ZipR r1 r2
ZipR (l :-> t1 ': r1) _ = TypeError (TL.Text "Row types with different label sets cannot be zipped"
:$$: TL.Text "For one, the label " :<>: ShowType l :<>: TL.Text " is not in both lists.")
ZipR '[] (l :-> t ': r) = TypeError (TL.Text "Row types with different label sets cannot be zipped"
:$$: TL.Text "For one, the label " :<>: ShowType l :<>: TL.Text " is not in both lists.")
type family Inject (l :: LT k) (r :: [LT k]) where
Inject (l :-> t) '[] = (l :-> t ': '[])
Inject (l :-> t) (l :-> t' ': x) = TypeError (TL.Text "Cannot inject a label into a row type that already has that label"
:$$: TL.Text "The label " :<>: ShowType l :<>: TL.Text " was already assigned the type "
:<>: ShowType t' :<>: TL.Text " and is now trying to be assigned the type "
:<>: ShowType t :<>: TL.Text ".")
Inject (l :-> t) (l' :-> t' ': x) =
Ifte (l <=.? l')
(l :-> t ': l' :-> t' ': x)
(l' :-> t' ': Inject (l :-> t) x)
-- | Type level Row modification helper
type family ModifyR (l :: Symbol) (a :: k) (ρ :: [LT k]) :: [LT k] where
ModifyR l a (l :-> a' ': ρ) = l :-> a ': ρ
ModifyR l a (l' :-> a' ': ρ) = l' :-> a' ': ModifyR l a ρ
ModifyR l a '[] = TypeError (TL.Text "Tried to modify the label " :<>: ShowType l
:<>: TL.Text ", but it does not appear in the row-type.")
type family Ifte (c :: Bool) (t :: k) (f :: k) where
Ifte True t f = t
Ifte False t f = f
type family Get (l :: Symbol) (r :: [LT k]) where
Get l '[] = TypeError (TL.Text "No such field: " :<>: ShowType l)
Get l (l :-> t ': x) = t
Get l (l' :-> t ': x) = Get l x
type family Remove (l :: Symbol) (r :: [LT k]) where
Remove l r = RemoveT l r r
type family RemoveT (l :: Symbol) (r :: [LT k]) (r_orig :: [LT k]) where
RemoveT l (l :-> t ': x) _ = x
RemoveT l (l' :-> t ': x) r = l' :-> t ': RemoveT l x r
RemoveT l '[] r = TypeError (TL.Text "Cannot remove a label that does not occur in the row type."
:$$: TL.Text "The label " :<>: ShowType l :<>: TL.Text " is not in "
:<>: ShowType r)
type family LacksR (l :: Symbol) (r :: [LT k]) (r_orig :: [LT k]) :: Constraint where
LacksR l '[] _ = Unconstrained
LacksR l (l :-> t ': x) r = TypeError (TL.Text "The label " :<>: ShowType l
:<>: TL.Text " already exists in " :<>: ShowType r)
LacksR l (l' :-> _ ': x) r = Ifte (l <=.? l') Unconstrained (LacksR l x r)
type family Merge (l :: [LT k]) (r :: [LT k]) where
Merge '[] r = r
Merge l '[] = l
Merge (h :-> a ': tl) (h :-> a ': tr) =
TypeError (TL.Text "The label " :<>: ShowType h :<>: TL.Text " (of type "
:$$: ShowType a :<>: TL.Text ") has duplicate assignments.")
Merge (h :-> a ': tl) (h :-> b ': tr) =
TypeError (TL.Text "The label " :<>: ShowType h :<>: TL.Text " has conflicting assignments."
:$$: TL.Text "Its type is both " :<>: ShowType a :<>: TL.Text " and " :<>: ShowType b :<>: TL.Text ".")
Merge (hl :-> al ': tl) (hr :-> ar ': tr) =
Ifte (hl <=.? hr)
(hl :-> al ': Merge tl (hr :-> ar ': tr))
(hr :-> ar ': Merge (hl :-> al ': tl) tr)
type family MinJoinR (l :: [LT k]) (r :: [LT k]) where
MinJoinR '[] r = r
MinJoinR l '[] = l
MinJoinR (h :-> a ': tl) (h :-> a ': tr) =
(h :-> a ': MinJoinR tl tr)
MinJoinR (h :-> a ': tl) (h :-> b ': tr) =
TypeError (TL.Text "The label " :<>: ShowType h :<>: TL.Text " has conflicting assignments."
:$$: TL.Text "Its type is both " :<>: ShowType a :<>: TL.Text " and " :<>: ShowType b :<>: TL.Text ".")
MinJoinR (hl :-> al ': tl) (hr :-> ar ': tr) =
Ifte (CmpSymbol hl hr == 'LT)
(hl :-> al ': MinJoinR tl (hr :-> ar ': tr))
(hr :-> ar ': MinJoinR (hl :-> al ': tl) tr)
type family ConstUnionR (l :: [LT k]) (r :: [LT k]) where
ConstUnionR '[] r = r
ConstUnionR l '[] = l
ConstUnionR (h :-> a ': tl) (h :-> b ': tr) =
(h :-> a ': ConstUnionR tl tr)
ConstUnionR (hl :-> al ': tl) (hr :-> ar ': tr) =
Ifte (CmpSymbol hl hr == 'LT)
(hl :-> al ': ConstUnionR tl (hr :-> ar ': tr))
(hr :-> ar ': ConstUnionR (hl :-> al ': tl) tr)
-- | Returns the left list with all of the elements from the right list removed.
type family Diff (l :: [LT k]) (r :: [LT k]) where
Diff '[] r = '[]
Diff l '[] = l
Diff (l :-> al ': tl) (l :-> al ': tr) = Diff tl tr
Diff (hl :-> al ': tl) (hr :-> ar ': tr) =
Ifte (hl <=.? hr)
(hl :-> al ': Diff tl (hr :-> ar ': tr))
(Diff (hl :-> al ': tl) tr)
-- | There doesn't seem to be a (<=.?) :: Symbol -> Symbol -> Bool,
-- so here it is in terms of other ghc-7.8 type functions
type a <=.? b = (CmpSymbol a b == 'LT)
-- | A lower fixity operator for type equality
infix 4 ≈
type a ≈ b = a ~ b