module Data.Row.Records
(
Label(..)
, KnownSymbol, AllUniqueLabels, WellBehaved
, Rec, Row, Empty, type (≈)
, empty
, type (.==), (.==), pattern (:==), unSingleton
, default', defaultA
, fromLabels, fromLabelsA, fromLabelsMapA
, extend, Extend, Lacks, type (.\)
, type (.-), (.-)
, restrict, split
, update, focus, multifocus, Modify, rename, Rename
, HasType, type (.!), (.!)
, type (.+), (.+), Disjoint, pattern (:+)
, toNative, fromNative
, Map, map, map'
, transform, transform'
, Forall, erase, eraseWithLabels, eraseZip, eraseToHashMap
, Zip, zip
, sequence, sequence'
, compose, uncompose
, compose', uncompose'
, labels, labels'
, unsafeRemove, unsafeInjectFront
)
where
import Prelude hiding (map, sequence, zip)
import Control.DeepSeq (NFData(..), deepseq)
import Data.Constraint ((\\))
import Data.Functor.Compose
import Data.Functor.Const
import Data.Functor.Identity
import Data.Functor.Product
import Data.Hashable
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as M
import qualified Data.List as L
import Data.Proxy
import Data.String (IsString)
import Data.Text (Text)
import qualified GHC.Generics as G
import GHC.TypeLits
import Unsafe.Coerce
import Data.Row.Internal
newtype Rec (r :: Row *) where
OR :: HashMap Text HideType -> Rec r
instance Forall r Show => Show (Rec r) where
showsPrec p r =
case eraseWithLabels @Show (showsPrec 7) r of
[] ->
showString "empty"
xs ->
showParen
(p > 6)
(mconcat (L.intersperse (showString " .+ ") (L.map binds xs)))
where
binds (label, value) =
showChar '#' .
showString label .
showString " .== " .
value
instance Forall r Eq => Eq (Rec r) where
r == r' = and $ eraseZip @Eq (==) r r'
instance (Forall r Eq, Forall r Ord) => Ord (Rec r) where
compare m m' = cmp $ eraseZip @Ord compare m m'
where cmp l | [] <- l' = EQ
| a : _ <- l' = a
where l' = dropWhile (== EQ) l
instance (Forall r Bounded, AllUniqueLabels r) => Bounded (Rec r) where
minBound = default' @Bounded minBound
maxBound = default' @Bounded maxBound
instance Forall r NFData => NFData (Rec r) where
rnf r = getConst $ metamorph @_ @r @NFData @Rec @(Const ()) @Identity Proxy empty doUncons doCons r
where empty = const $ Const ()
doUncons l r = (Identity $ r .! l, unsafeRemove l r)
doCons _ x r = deepseq x $ deepseq r $ Const ()
empty :: Rec Empty
empty = OR M.empty
infix 7 .==
(.==) :: KnownSymbol l => Label l -> a -> Rec (l .== a)
l .== a = extend l a empty
{-# COMPLETE (:==) #-}
infix 7 :==
pattern (:==) :: forall l a. KnownSymbol l => Label l -> a -> Rec (l .== a)
pattern l :== a <- (unSingleton @l @a -> (l, a)) where
(:==) l a = l .== a
unSingleton :: forall l a. KnownSymbol l => Rec (l .== a) -> (Label l, a)
unSingleton r = (l, r .! l) where l = Label @l
extend :: forall a l r. KnownSymbol l => Label l -> a -> Rec r -> Rec (Extend l a r)
extend (toKey -> l) a (OR m) = OR $ M.insert l (HideType a) m
update :: (KnownSymbol l, r .! l ≈ a) => Label l -> a -> Rec r -> Rec r
update (toKey -> l) a (OR m) = OR $ M.adjust f l m where f = const (HideType a)
focus :: (Functor f, KnownSymbol l) => Label l -> (r .! l -> f a) -> Rec r -> f (Rec (Modify l a r))
focus (toKey -> l) f (OR m) = case m M.! l of
HideType x -> OR . flip (M.insert l) m . HideType <$> f (unsafeCoerce x)
multifocus :: forall u v r f.
( Functor f
, Disjoint u r
, Disjoint v r)
=> (Rec u -> f (Rec v)) -> Rec (u .+ r) -> f (Rec (v .+ r))
multifocus f (u :+ r) = (.+ r) <$> f u
rename :: (KnownSymbol l, KnownSymbol l') => Label l -> Label l' -> Rec r -> Rec (Rename l l' r)
rename (toKey -> l) (toKey -> l') (OR m) = OR $ M.insert l' (m M.! l) $ M.delete l m
(.!) :: KnownSymbol l => Rec r -> Label l -> r .! l
OR m .! (toKey -> a) = case m M.! a of
HideType x -> unsafeCoerce x
infixl 6 .-
(.-) :: KnownSymbol l => Rec r -> Label l -> Rec (r .- l)
OR m .- (toKey -> a) = OR $ M.delete a m
infixl 6 .+
(.+) :: Rec l -> Rec r -> Rec (l .+ r)
OR l .+ OR r = OR $ M.unionWith (error "Impossible") l r
{-# COMPLETE (:+) #-}
infixl 6 :+
pattern (:+) :: forall l r. Disjoint l r => Rec l -> Rec r -> Rec (l .+ r)
pattern l :+ r <- (split @l -> (l, r)) where
(:+) l r = l .+ r
split :: forall s r. (Forall s Unconstrained1, Subset s r)
=> Rec r -> (Rec s, Rec (r .\\ s))
split (OR m) = (OR $ M.intersection m labelMap, OR $ M.difference m labelMap)
where labelMap = M.fromList $ L.zip (labels @s @Unconstrained1) (repeat ())
restrict :: forall r r'. (Forall r Unconstrained1, Subset r r') => Rec r' -> Rec r
restrict = fst . split
unsafeRemove :: KnownSymbol l => Label l -> Rec r -> Rec (r .- l)
unsafeRemove _ (OR m) = OR m
type IPair = Product Identity Identity
iPair :: τ -> τ -> IPair τ
iPair = (. Identity) . Pair . Identity
unIPair :: IPair τ -> (τ, τ)
unIPair (Pair (Identity x) (Identity y)) = (x,y)
erase :: forall c ρ b. Forall ρ c => (forall a. c a => a -> b) -> Rec ρ -> [b]
erase f = fmap (snd @String) . eraseWithLabels @c f
eraseWithLabels :: forall c ρ s b. (Forall ρ c, IsString s) => (forall a. c a => a -> b) -> Rec ρ -> [(s,b)]
eraseWithLabels f = getConst . metamorph @_ @ρ @c @Rec @(Const [(s,b)]) @Identity Proxy doNil doUncons doCons
where doNil _ = Const []
doUncons l r = (Identity $ r .! l, unsafeRemove l r)
doCons :: forall ℓ τ ρ. (KnownSymbol ℓ, c τ)
=> Label ℓ -> Identity τ -> Const [(s,b)] ('R ρ) -> Const [(s,b)] ('R (ℓ :-> τ ': ρ))
doCons l (Identity x) (Const c) = Const $ (show' l, f x) : c
eraseZip :: forall c ρ b. Forall ρ c => (forall a. c a => a -> a -> b) -> Rec ρ -> Rec ρ -> [b]
eraseZip f x y = getConst $ metamorph @_ @ρ @c @(Product Rec Rec) @(Const [b]) @IPair Proxy (const $ Const []) doUncons doCons (Pair x y)
where doUncons l (Pair r1 r2) = (iPair a b, Pair r1' r2')
where (a, r1') = (r1 .! l, unsafeRemove l r1)
(b, r2') = (r2 .! l, unsafeRemove l r2)
doCons :: forall ℓ τ ρ. c τ
=> Label ℓ -> IPair τ -> Const [b] ('R ρ) -> Const [b] ('R (ℓ :-> τ ': ρ))
doCons _ (unIPair -> x) (Const c) = Const $ uncurry f x : c
eraseToHashMap :: forall c r s b. (IsString s, Eq s, Hashable s, Forall r c) =>
(forall a . c a => a -> b) -> Rec r -> HashMap s b
eraseToHashMap f r = M.fromList $ eraseWithLabels @c f r
newtype RMap (f :: * -> *) (ρ :: Row *) = RMap { unRMap :: Rec (Map f ρ) }
newtype RMap2 (f :: * -> *) (g :: * -> *) (ρ :: Row *) = RMap2 { unRMap2 :: Rec (Map f (Map g ρ)) }
map :: forall c f r. Forall r c => (forall a. c a => a -> f a) -> Rec r -> Rec (Map f r)
map f = unRMap . metamorph @_ @r @c @Rec @(RMap f) @Identity Proxy doNil doUncons doCons
where
doNil _ = RMap empty
doUncons l r = (Identity $ r .! l, unsafeRemove l r)
doCons :: forall ℓ τ ρ. (KnownSymbol ℓ, c τ)
=> Label ℓ -> Identity τ -> RMap f ('R ρ) -> RMap f ('R (ℓ :-> τ ': ρ))
doCons l (Identity v) (RMap r) = RMap (unsafeInjectFront l (f v) r)
map' :: forall f r. Forall r Unconstrained1 => (forall a. a -> f a) -> Rec r -> Rec (Map f r)
map' = map @Unconstrained1
transform :: forall c r (f :: * -> *) (g :: * -> *). Forall r c => (forall a. c a => f a -> g a) -> Rec (Map f r) -> Rec (Map g r)
transform f = unRMap . metamorph @_ @r @c @(RMap f) @(RMap g) @f Proxy doNil doUncons doCons . RMap
where
doNil _ = RMap empty
doUncons l (RMap r) = (r .! l, RMap $ unsafeRemove l r)
doCons :: forall ℓ τ ρ. (KnownSymbol ℓ, c τ)
=> Label ℓ -> f τ -> RMap g ('R ρ) -> RMap g ('R (ℓ :-> τ ': ρ))
doCons l v (RMap r) = RMap (unsafeInjectFront l (f v) r)
transform' :: forall r (f :: * -> *) (g :: * -> *). Forall r Unconstrained1 => (forall a. f a -> g a) -> Rec (Map f r) -> Rec (Map g r)
transform' = transform @Unconstrained1 @r
sequence' :: forall f r c. (Forall r c, Applicative f)
=> Rec (Map f r) -> f (Rec r)
sequence' = getCompose . metamorph @_ @r @c @(RMap f) @(Compose f Rec) @f Proxy doNil doUncons doCons . RMap
where
doNil _ = Compose (pure empty)
doUncons l (RMap r) = (r .! l, RMap $ unsafeRemove l r)
doCons l fv (Compose fr) = Compose $ unsafeInjectFront l <$> fv <*> fr
sequence :: forall f r. (Forall r Unconstrained1, Applicative f)
=> Rec (Map f r) -> f (Rec r)
sequence = sequence' @_ @_ @Unconstrained1
compose' :: forall c (f :: * -> *) (g :: * -> *) (r :: Row *) . Forall r c
=> Rec (Map f (Map g r)) -> Rec (Map (Compose f g) r)
compose' = unRMap . metamorph @_ @r @c @(RMap2 f g) @(RMap (Compose f g)) @(Compose f g) Proxy doNil doUncons doCons . RMap2
where
doNil _ = RMap empty
doUncons l (RMap2 r) = (Compose $ r .! l, RMap2 $ unsafeRemove l r)
doCons l v (RMap r) = RMap $ unsafeInjectFront l v r
compose :: forall (f :: * -> *) (g :: * -> *) r . Forall r Unconstrained1
=> Rec (Map f (Map g r)) -> Rec (Map (Compose f g) r)
compose = compose' @Unconstrained1 @f @g @r
uncompose' :: forall c (f :: * -> *) (g :: * -> *) r . Forall r c
=> Rec (Map (Compose f g) r) -> Rec (Map f (Map g r))
uncompose' = unRMap2 . metamorph @_ @r @c @(RMap (Compose f g)) @(RMap2 f g) @(Compose f g) Proxy doNil doUncons doCons . RMap
where
doNil _ = RMap2 empty
doUncons l (RMap r) = (r .! l, RMap $ unsafeRemove l r)
doCons l (Compose v) (RMap2 r) = RMap2 $ unsafeInjectFront l v r
uncompose :: forall (f :: * -> *) (g :: * -> *) r . Forall r Unconstrained1
=> Rec (Map (Compose f g) r) -> Rec (Map f (Map g r))
uncompose = uncompose' @Unconstrained1 @f @g @r
newtype RZipPair (ρ1 :: Row *) (ρ2 :: Row *) = RZipPair { unRZipPair :: Rec (Zip ρ1 ρ2) }
zip :: forall r1 r2. Forall2 r1 r2 Unconstrained1 => Rec r1 -> Rec r2 -> Rec (Zip r1 r2)
zip r1 r2 = unRZipPair $ metamorph2 @_ @r1 @r2 @Unconstrained1 @Rec @Rec @RZipPair @Identity @Identity Proxy Proxy doNil doUncons doCons r1 r2
where
doNil _ _ = RZipPair empty
doUncons l r1 r2 = ((Identity $ r1 .! l, unsafeRemove l r1), (Identity $ r2 .! l, unsafeRemove l r2))
doCons l (Identity v1) (Identity v2) (RZipPair r) = RZipPair $ unsafeInjectFront l (v1, v2) r
unsafeInjectFront :: KnownSymbol l => Label l -> a -> Rec (R r) -> Rec (R (l :-> a ': r))
unsafeInjectFront (toKey -> a) b (OR m) = OR $ M.insert a (HideType b) m
default' :: forall c ρ. (Forall ρ c, AllUniqueLabels ρ) => (forall a. c a => a) -> Rec ρ
default' v = runIdentity $ defaultA @c $ pure v
defaultA :: forall c f ρ. (Applicative f, Forall ρ c, AllUniqueLabels ρ)
=> (forall a. c a => f a) -> f (Rec ρ)
defaultA v = fromLabelsA @c $ pure v
fromLabels :: forall c ρ. (Forall ρ c, AllUniqueLabels ρ)
=> (forall l a. (KnownSymbol l, c a) => Label l -> a) -> Rec ρ
fromLabels f = runIdentity $ fromLabelsA @c $ (pure .) f
fromLabelsA :: forall c f ρ. (Applicative f, Forall ρ c, AllUniqueLabels ρ)
=> (forall l a. (KnownSymbol l, c a) => Label l -> f a) -> f (Rec ρ)
fromLabelsA mk = getCompose $ metamorph @_ @ρ @c @(Const ()) @(Compose f Rec) @(Const ()) Proxy doNil doUncons doCons (Const ())
where doNil _ = Compose $ pure empty
doUncons _ _ = (Const (), Const ())
doCons :: forall ℓ τ ρ. (KnownSymbol ℓ, c τ)
=> Label ℓ -> Const () τ -> Compose f Rec ('R ρ) -> Compose f Rec ('R (ℓ :-> τ ': ρ))
doCons l _ (Compose r) = Compose $ unsafeInjectFront l <$> mk l <*> r
fromLabelsMapA :: forall c f g ρ. (Applicative f, Forall ρ c, AllUniqueLabels ρ)
=> (forall l a. (KnownSymbol l, c a) => Label l -> f (g a)) -> f (Rec (Map g ρ))
fromLabelsMapA f = fromLabelsA @(IsA c g) @f @(Map g ρ) inner
\\ mapForall @g @c @ρ
\\ uniqueMap @g @ρ
where inner :: forall l a. (KnownSymbol l, IsA c g a) => Label l -> f a
inner l = case as @c @g @a of As -> f l
class ToNative a ρ where
toNative' :: Rec ρ -> a x
instance ToNative cs ρ => ToNative (G.D1 m cs) ρ where
toNative' xs = G.M1 $ toNative' xs
instance ToNative cs ρ => ToNative (G.C1 m cs) ρ where
toNative' xs = G.M1 $ toNative' xs
instance (KnownSymbol name, ρ .! name ≈ t)
=> ToNative (G.S1 ('G.MetaSel ('Just name) p s l) (G.Rec0 t)) ρ where
toNative' r = G.M1 $ G.K1 $ r .! (Label @name)
instance (ToNative l ρ, ToNative r ρ)
=> ToNative (l G.:*: r) ρ where
toNative' r = toNative' r G.:*: toNative' r
toNative :: forall t ρ. (G.Generic t, ToNative (G.Rep t) ρ) => Rec ρ -> t
toNative = G.to . toNative'
class FromNative a ρ where
fromNative' :: a x -> Rec ρ
instance FromNative cs ρ => FromNative (G.D1 m cs) ρ where
fromNative' (G.M1 xs) = fromNative' xs
instance FromNative cs ρ => FromNative (G.C1 m cs) ρ where
fromNative' (G.M1 xs) = fromNative' xs
instance (KnownSymbol name, ρ ≈ name .== t)
=> FromNative (G.S1 ('G.MetaSel ('Just name) p s l) (G.Rec0 t)) ρ where
fromNative' (G.M1 (G.K1 x)) = (Label @name) .== x
instance (FromNative l ρ₁, FromNative r ρ₂, ρ ≈ ρ₁ .+ ρ₂)
=> FromNative (l G.:*: r) ρ where
fromNative' (x G.:*: y) = fromNative' @l @ρ₁ x .+ fromNative' @r @ρ₂ y
fromNative :: forall t ρ. (G.Generic t, FromNative (G.Rep t) ρ) => t -> Rec ρ
fromNative = fromNative' . G.from