module Data.Row.Internal
(
Row(..)
, Label(..)
, KnownSymbol
, LT(..)
, Empty
, HideType(..)
, Extend, Modify, Rename
, type (.\), type (.!), type (.-), type (.+), type (.\\), type (.==)
, Lacks, HasType
, Labels, labels
, Forall(..), Forall2(..)
, Unconstrained1
, show'
, toKey
, type (≈)
, WellBehaved, AllUniqueLabels, Zip, Map, Subset, Disjoint
)
where
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 GHC.Exts
import GHC.OverloadedLabels
import GHC.TypeLits
import qualified GHC.TypeLits as TL
newtype Row a = R [LT a]
data LT a = Symbol :-> a
data Label (s :: Symbol) = Label
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
show' :: (IsString s, Show a) => a -> s
show' = fromString . show
toKey :: forall s. KnownSymbol s => Label s -> Text
toKey = Text.pack . symbolVal
type Empty = R '[]
data HideType where
HideType :: a -> HideType
infixl 4 .\
type family (r :: Row *) .\ (l :: Symbol) :: Constraint where
R r .\ l = LacksR l r r
type family Extend (l :: Symbol) (a :: *) (r :: Row *) :: Row * where
Extend l a (R x) = R (Inject (l :-> a) x)
type family Modify (l :: Symbol) (a :: *) (r :: Row *) :: Row * where
Modify l a (R ρ) = R (ModifyR l a ρ)
type family Rename (l :: Symbol) (l' :: Symbol) (r :: Row *) :: Row * where
Rename l l' r = Extend l' (r .! l) (r .- l)
infixl 5 .!
type family (r :: Row *) .! (t :: Symbol) :: * where
R r .! l = Get l r
infixl 6 .-
type family (r :: Row *) .- (s :: Symbol) :: Row * where
R r .- l = R (Remove l r)
infixl 6 .+
type family (l :: Row *) .+ (r :: Row *) :: Row * where
R l .+ R r = R (Merge l r)
infixl 6 .\\
type family (l :: Row *) .\\ (r :: Row *) :: Row * where
R l .\\ R r = R (Diff l r)
class Lacks (l :: Symbol) (r :: Row *)
instance (r .\ l) => Lacks l r
class (r .! l ≈ a) => HasType l a r
instance (r .! l ≈ a) => HasType l a r
infix 7 .==
type (l :: Symbol) .== (a :: *) = Extend l a Empty
type FoldStep ℓ τ ρ = ( Inject (ℓ :-> τ) ρ ≈ ℓ :-> τ ': ρ
, R ρ .\ ℓ
)
class Forall (r :: Row *) (c :: * -> Constraint) where
metamorph :: forall (f :: Row * -> *) (g :: Row * -> *) (h :: * -> *).
Proxy h
-> (f Empty -> g Empty)
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> (h τ, f ('R ρ)))
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ, FoldStep ℓ τ ρ) => Label ℓ -> h τ -> g ('R ρ) -> g ('R (ℓ :-> τ ': ρ)))
-> f r
-> g r
metamorph' :: forall (f :: Row * -> *) (g :: Row * -> *) (h :: * -> *).
Proxy h
-> (f Empty -> g Empty)
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> Either (h τ) (f ('R ρ)))
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ, FoldStep ℓ τ ρ) => Label ℓ -> Either (h τ) (g ('R ρ)) -> g ('R (ℓ :-> τ ': ρ)))
-> f r
-> g r
instance Forall (R '[]) c where
metamorph _ empty _ _ = empty
metamorph' _ empty _ _ = empty
instance (KnownSymbol ℓ, c τ, FoldStep ℓ τ ρ, Forall ('R ρ) c) => Forall ('R (ℓ :-> τ ': ρ)) c where
metamorph :: forall (f :: Row * -> *) (g :: Row * -> *) (h :: * -> *).
Proxy h
-> (f Empty -> g Empty)
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> (h τ, f ('R ρ)))
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ, FoldStep ℓ τ ρ) => Label ℓ -> h τ -> g ('R ρ) -> g ('R (ℓ :-> τ ': ρ)))
-> f ('R (ℓ :-> τ ': ρ))
-> g ('R (ℓ :-> τ ': ρ))
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 * -> *) (g :: Row * -> *) (h :: * -> *).
Proxy h
-> (f Empty -> g Empty)
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ) => Label ℓ -> f ('R (ℓ :-> τ ': ρ)) -> Either (h τ) (f ('R ρ)))
-> (forall ℓ τ ρ. (KnownSymbol ℓ, c τ, FoldStep ℓ τ ρ) => Label ℓ -> Either (h τ) (g ('R ρ)) -> g ('R (ℓ :-> τ ': ρ)))
-> f ('R (ℓ :-> τ ': ρ))
-> g ('R (ℓ :-> τ ': ρ))
metamorph' _ empty uncons cons r = cons Label $ metamorph' @('R ρ) @c @_ @_ @h Proxy empty uncons cons <$> uncons Label r
class Forall2 (r1 :: Row *) (r2 :: Row *) (c :: * -> Constraint) where
metamorph2 :: forall (f :: Row * -> *) (g :: Row * -> *) (h :: Row * -> Row * -> *)
(f' :: * -> *) (g' :: * -> *).
Proxy f' -> Proxy g'
-> (f Empty -> g Empty -> h Empty Empty)
-> (forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1, c τ2)
=> Label ℓ
-> f ('R (ℓ :-> τ1 ': ρ1))
-> g ('R (ℓ :-> τ2 ': ρ2))
-> ((f' τ1, f ('R ρ1)), (g' τ2, g ('R ρ2))))
-> (forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1, c τ2)
=> Label ℓ -> f' τ1 -> g' τ2 -> h ('R ρ1) ('R ρ2) -> h ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2)))
-> f r1 -> g r2 -> h r1 r2
instance Forall2 (R '[]) (R '[]) c where
metamorph2 _ _ empty _ _ = empty
instance (KnownSymbol ℓ, c τ1, c τ2, Forall2 ('R ρ1) ('R ρ2) c)
=> Forall2 ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2)) c where
metamorph2 f g empty uncons cons r1 r2 = cons (Label @ℓ) t1 t2 $ metamorph2 @('R ρ1) @('R ρ2) @c f g empty uncons cons r1' r2'
where ((t1, r1'), (t2, r2')) = uncons (Label @ℓ) r1 r2
class Unconstrained
instance Unconstrained
class Unconstrained1 a
instance Unconstrained1 a
type family Labels (r :: Row a) where
Labels (R '[]) = '[]
Labels (R (l :-> a ': xs)) = l ': Labels (R xs)
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
type WellBehaved ρ = (Forall ρ Unconstrained1, AllUniqueLabels ρ)
type family AllUniqueLabels (r :: Row *) :: Constraint where
AllUniqueLabels (R r) = AllUniqueLabelsR r
type family AllUniqueLabelsR (r :: [LT *]) :: 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)
type family Subset (r1 :: Row *) (r2 :: Row *) :: Constraint where
Subset (R r1) (R r2) = SubsetR r1 r2
type family SubsetR (r1 :: [LT *]) (r2 :: [LT *]) :: 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)
type Disjoint l r = ( WellBehaved l
, WellBehaved r
, Subset l (l .+ r)
, Subset r (l .+ r)
, l .+ r .\\ l ≈ r
, l .+ r .\\ r ≈ l)
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
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 *) (r :: [LT *]) 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 family ModifyR (l :: Symbol) (a :: *) (ρ :: [LT *]) :: [LT *] 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 *]) 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 *]) where
Remove l r = RemoveT l r r
type family RemoveT (l :: Symbol) (r :: [LT *]) (r_orig :: [LT *]) 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 *]) (r_orig :: [LT *]) :: Constraint where
LacksR l '[] r = Unconstrained
LacksR l (l :-> t ': x) r = TypeError (TL.Text "The label " :<>: ShowType l
:<>: TL.Text " already exists in " :<>: ShowType r)
LacksR l (p ': x) r = LacksR l x r
type family Merge (l :: [LT *]) (r :: [LT *]) where
Merge '[] r = r
Merge l '[] = l
Merge (h :-> a ': tl) (h :-> a ': tr) =
(h :-> a ': Merge tl tr)
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 Diff (l :: [LT *]) (r :: [LT *]) 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)
type a <=.? b = (CmpSymbol a b == 'LT)
infix 4 ≈
type a ≈ b = a ~ b