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 (:+)
, type (.//), (.//)
, toNative, toNativeExact, fromNative
, toDynamicMap, fromDynamicMap
, Map, map, map', mapF
, 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.Dynamic
import Data.Functor.Compose
import Data.Functor.Const
import Data.Functor.Identity
import Data.Functor.Product
import Data.Generics.Product.Fields (HasField(..), HasField'(..))
import Data.Hashable
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as M
import qualified Data.List as L
import Data.Monoid (Endo(..), appEndo)
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)
(appEndo $ foldMap Endo (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 ::
( KnownSymbol l
, r' .! l ≈ b
, r .! l ≈ a
, r' ~ Modify l b r
, r ~ Modify l a r'
, Functor f)
=> Label l -> (a -> f b) -> Rec r -> f (Rec 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
(.//) :: Rec r -> Rec r' -> Rec (r .// r')
OR l .// OR r = OR $ M.union 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)
newtype RFMap (g :: k1 -> k2) (ϕ :: Row (k2 -> *)) (ρ :: Row k1) = RFMap { unRFMap :: Rec (Ap ϕ (Map g ρ)) }
newtype RecAp (ϕ :: Row (k -> *)) (ρ :: Row k) = RecAp (Rec (Ap ϕ ρ))
newtype App (f :: k -> *) (a :: k) = App (f a)
mapF :: forall c g (ϕ :: Row (k -> *)) (ρ :: Row k). BiForall ϕ ρ c
=> (forall f a. (c f a) => f a -> f (g a))
-> Rec (Ap ϕ ρ)
-> Rec (Ap ϕ (Map g ρ))
mapF f = unRFMap . biMetamorph @_ @_ @ϕ @ρ @c @RecAp @(RFMap g) @App Proxy doNil doUncons doCons . RecAp
where
doNil _ = RFMap empty
doUncons l (RecAp r) = (App $ r .! l, RecAp $ unsafeRemove l r)
doCons :: forall ℓ τ1 τ2 ρ1 ρ2. (KnownSymbol ℓ, c τ1 τ2)
=> Label ℓ -> App τ1 τ2 -> RFMap g ('R ρ1) ('R ρ2) -> RFMap g ('R (ℓ :-> τ1 ': ρ1)) ('R (ℓ :-> τ2 ': ρ2))
doCons l (App v) (RFMap r) = RFMap (unsafeInjectFront l (f @τ1 @τ2 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 RecPair (ρ1 :: Row *) (ρ2 :: Row *) = RecPair (Rec ρ1, Rec ρ2)
newtype RZipPair (ρ1 :: Row *) (ρ2 :: Row *) = RZipPair { unRZipPair :: Rec (Zip ρ1 ρ2) }
zip :: forall r1 r2. BiForall r1 r2 Unconstrained2 => Rec r1 -> Rec r2 -> Rec (Zip r1 r2)
zip r1 r2 = unRZipPair $ biMetamorph @_ @_ @r1 @r2 @Unconstrained2 @RecPair @RZipPair @(,) Proxy doNil doUncons doCons $ RecPair (r1, r2)
where
doNil _ = RZipPair empty
doUncons l (RecPair (r1, r2)) = ((r1 .! l, r2 .! l), RecPair (unsafeRemove l r1, unsafeRemove l r2))
doCons l (v1, 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
{-# INLINE unsafeInjectFront #-}
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
toDynamicMap :: Forall r Typeable => Rec r -> HashMap Text Dynamic
toDynamicMap = eraseToHashMap @Typeable @_ @Text @Dynamic toDyn
fromDynamicMap :: (AllUniqueLabels r, Forall r Typeable)
=> HashMap Text Dynamic -> Maybe (Rec r)
fromDynamicMap m = fromLabelsA @Typeable
$ \ (toKey -> k) -> M.lookup k m >>= fromDynamic
instance GenericRec r => G.Generic (Rec r) where
type Rep (Rec r) =
G.D1 ('G.MetaData "Rec" "Data.Row.Records" "row-types" 'False)
(G.C1 ('G.MetaCons "Rec" 'G.PrefixI 'True)
(RepRec r))
from = G.M1 . G.M1 . fromRec
to = toRec . G.unM1 . G.unM1
type family RepRec (r :: Row *) :: * -> * where
RepRec (R '[]) = G.U1
RepRec (R (name :-> t ': '[])) = G.S1
('G.MetaSel ('Just name) 'G.NoSourceUnpackedness 'G.NoSourceStrictness 'G.DecidedLazy)
(G.Rec0 t)
RepRec (R (name :-> t ': r)) = (G.S1
('G.MetaSel ('Just name) 'G.NoSourceUnpackedness 'G.NoSourceStrictness 'G.DecidedLazy)
(G.Rec0 t)) G.:*: RepRec (R r)
class GenericRec r where
fromRec :: Rec r -> RepRec r x
toRec :: RepRec r x -> Rec r
instance GenericRec Empty where
fromRec _ = G.U1
toRec _ = empty
instance KnownSymbol name => GenericRec (R '[name :-> t]) where
fromRec (_ :== a) = G.M1 (G.K1 a)
toRec (G.M1 (G.K1 a)) = (Label @name) :== a
instance {-# OVERLAPPABLE #-}
( GenericRec (R r)
, KnownSymbol name
, r ~ (name' :-> t' ': r')
) => GenericRec (R (name :-> t ': r)) where
fromRec r = G.M1 (G.K1 (r .! Label @name)) G.:*: fromRec (unsafeRemove @name Label r)
toRec (G.M1 (G.K1 a) G.:*: r) = unsafeInjectFront (Label @name) a (toRec r)
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 ToNative G.U1 ρ where
toNative' _ = G.U1
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 ToNativeExact a ρ where
toNativeExact' :: Rec ρ -> a x
instance ToNativeExact cs ρ => ToNativeExact (G.D1 m cs) ρ where
toNativeExact' xs = G.M1 $ toNativeExact' xs
instance ToNativeExact cs ρ => ToNativeExact (G.C1 m cs) ρ where
toNativeExact' xs = G.M1 $ toNativeExact' xs
instance ToNativeExact G.U1 Empty where
toNativeExact' _ = G.U1
instance (KnownSymbol name, ρ ≈ name .== t)
=> ToNativeExact (G.S1 ('G.MetaSel ('Just name) p s l) (G.Rec0 t)) ρ where
toNativeExact' r = G.M1 $ G.K1 $ r .! (Label @name)
instance (ToNativeExact l ρ₁, ToNativeExact r ρ₂, ρ ≈ ρ₁ .+ ρ₂, Disjoint ρ₁ ρ₂)
=> ToNativeExact (l G.:*: r) ρ where
toNativeExact' r = toNativeExact' r1 G.:*: toNativeExact' r2
where
(r1 :: Rec ρ₁) :+ (r2 :: Rec ρ₂) = r
toNativeExact :: forall t ρ. (G.Generic t, ToNativeExact (G.Rep t) ρ) => Rec ρ -> t
toNativeExact = G.to . toNativeExact'
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 FromNative G.U1 Empty where
fromNative' G.U1 = empty
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
instance {-# OVERLAPPING #-}
( KnownSymbol name
, r' .! name ≈ b
, r .! name ≈ a
, r' ~ Modify name b r
, r ~ Modify name a r')
=> HasField name (Rec r) (Rec r') a b where
field = focus (Label @name)
{-# INLINE field #-}
instance {-# OVERLAPPING #-}
( KnownSymbol name
, r .! name ≈ a
, r ~ Modify name a r)
=> HasField' name (Rec r) a where
field' = focus (Label @name)
{-# INLINE field' #-}