module Data.Row.Records
(
Label(..)
, KnownSymbol, AllUniqueLabels, WellBehaved
, Rec, Row, Empty, type (≈)
, empty
, type (.==), (.==), pattern (:==), unSingleton
, default', defaultA
, fromLabels, fromLabelsA
, 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 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 => 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