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 (:+)
, Map, map, map'
, transform, transform'
, Forall, erase, eraseWithLabels, eraseZip, eraseToHashMap
, Zip, zip
, sequence
, compose, uncompose
, labels
, unsafeRemove, unsafeInjectFront
)
where
import Prelude hiding (map, sequence, zip)
import Control.DeepSeq (NFData(..), deepseq)
import qualified Data.Constraint as 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 Data.List hiding (map, zip)
import qualified Data.List as L
import Data.Proxy
import Data.String (IsString)
import Data.Text (Text)
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
show r = "{ " ++ intercalate ", " binds ++ " }"
where binds = (\ (x, y) -> x ++ "=" ++ y) <$> eraseWithLabels @Show show r
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
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
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. (Forall r Unconstrained1, Applicative f) => Rec (Map f r) -> f (Rec r)
sequence = getCompose . metamorph @r @Unconstrained1 @(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
compose :: forall (f :: * -> *) g r . Forall r Unconstrained1 => Rec (Map f (Map g r)) -> Rec (Map (Compose f g) r)
compose = unRMap . metamorph @r @Unconstrained1 @(RMap2 f g) @(RMap (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
uncompose :: forall (f :: * -> *) g r . Forall r Unconstrained1 => Rec (Map (Compose f g) r) -> Rec (Map f (Map g r))
uncompose = unRMap2 . metamorph @r @Unconstrained1 @(RMap (Compose f g)) @(RMap2 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
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
Constraint.\\ mapForall @g @c @ρ
Constraint.\\ 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