{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
module Data.DocRecord
(
module Data.Vinyl.Core
, module Data.Vinyl.Lens
, module Data.Vinyl.Derived
, module Data.Vinyl.Curry
, type (++)
, type AllFst, type AllSnd
, PathWithType(..)
, FieldWithTag, fieldTag
, Field(..)
, Tagged(..)
, WithDoc
, PossiblyEmpty(..)
, PossiblyEmptyField
, pattern PEField
, type DocField
, pattern DocField
, DocRec
, FieldTypes
, IdentityField
, NamedField(..)
, NamedFieldTag(..)
, MissingValueReason(..)
, IntermediaryLevel
, FlattenedLevel
, HasField, Includes, EquivalentTo
, type Difference, type Intersection
, ToJSONFields
, RecBijection(..)
, ShowPath(..)
, ApplyRec(..)
, MD(..)
, type Fst
, type Snd
, removeDoc
, withoutDef
, getPossiblyEmpty
, chooseHighestPriority
, fld
, runcurryF
, runcurryAF
, docField
, itmLevel
, fieldPath
, fieldPathList
, fieldFromDef
, fieldNoDef
, singleton
, useDef
, fromJSONAs
, (^^.), (^^?), (^^?!), (%%~), (..~)
, renamedAs
, rsubset, rcast, rreplace
, rcastAs, rsplit, rsplitFrom, rdifference, rintersection
, PrefixPath(..), rinclude, (-.)
, rdrill
, rsplitDrill
, rfoldSubset
, funder
, runder
, (-/)
, withSameFields
, (&:)
, recFrom
, invertRecBij, (<<|>>), bijectField, bijectField', renameField, addConstField
, bijectUnder
, showDocumentation
) where
import Control.Applicative
import qualified Control.Category as Cat
import qualified Control.Lens as L
import Data.Aeson
import Data.Default
import qualified Data.HashMap.Strict as HM
import Data.Maybe (fromMaybe)
import Data.Proxy
import qualified Data.Text as T
import Data.Typeable
import Data.Vinyl.Core
import Data.Vinyl.Curry
import Data.Vinyl.Derived hiding (HasField, rfield, (=:))
import qualified Data.Vinyl.Functor as F
import Data.Vinyl.Lens (RElem, RSubset, rlens)
import qualified Data.Vinyl.Lens as VL
import Data.Vinyl.TypeLevel hiding (Fst, Snd)
import GHC.Exts (Constraint)
import GHC.TypeLits (ErrorMessage (..), KnownSymbol, Symbol,
TypeError, symbolVal)
data MissingValueReason = NoDefault | MissingValueInJSON
deriving (Show, Eq, Ord)
newtype PossiblyEmpty a = PE (Either MissingValueReason a)
deriving (Functor, Applicative)
instance (Eq a) => Eq (PossiblyEmpty a) where
PE (Right a) == PE (Right b) = a == b
PE (Left _) == PE (Left _) = True
_ == _ = False
instance Ord a => Ord (PossiblyEmpty a) where
PE (Right a) `compare` PE (Right b) = a `compare` b
PE (Left _) `compare` PE (Left _) = EQ
_ `compare` _ = LT
instance (Show (f (s:|:a)), ShowPath s)
=> Show (PossiblyEmpty (f (s:|:a))) where
show x = case x of
PE (Right a) -> show a
PE (Left r) ->
T.unpack (showPath (Proxy @s)) ++ " (empty: " ++ show r ++ ")\n"
instance (Semigroup a) => Semigroup (PossiblyEmpty a) where
PE (Right x) <> PE (Right y) = PE $ Right $ x<>y
PE (Left _) <> PE (Right x) = PE $ Right x
PE (Right x) <> PE (Left _) = PE $ Right x
PE (Left x) <> PE (Left y) = PE $ Left $ max x y
instance (Semigroup a) => Monoid (PossiblyEmpty a) where
mempty = PE (Left NoDefault)
data Tagged tag a = Tagged { tagFromTagged :: tag
, valueFromTagged :: a }
type WithDoc = Tagged T.Text
instance (Eq a) => Eq (Tagged tag a) where
Tagged _ a == Tagged _ b = a == b
instance (Ord a) => Ord (Tagged tag a) where
Tagged _ a `compare` Tagged _ b = a `compare` b
instance (Show a) => Show (Tagged tag a) where
show (Tagged _ a) = show a
instance (Semigroup a) => Semigroup (Tagged tag a) where
Tagged _ x <> Tagged tag y = Tagged tag (x<>y)
instance (Monoid tag, Monoid a) => Monoid (Tagged tag a) where
mempty = Tagged mempty mempty
instance Functor (Tagged tag) where
fmap f (Tagged d a) = Tagged d (f a)
instance (Monoid tag) => Applicative (Tagged tag) where
pure = Tagged mempty
Tagged t1 f <*> Tagged t2 x = Tagged (t1<>t2) (f x)
chooseHighestPriority
:: Ord a
=> F.Compose (Tagged a) (F.Compose (Tagged T.Text) f) x
-> F.Compose (Tagged a) (F.Compose (Tagged T.Text) f) x
-> F.Compose (Tagged a) (F.Compose (Tagged T.Text) f) x
chooseHighestPriority (F.Compose (Tagged s1 (F.Compose (Tagged doc1 f1))))
(F.Compose (Tagged s2 (F.Compose (Tagged doc2 f2)))) =
if s2 >= s1
then F.Compose (Tagged s2 (F.Compose (Tagged doc f2)))
else F.Compose (Tagged s1 (F.Compose (Tagged doc f1)))
where
doc | doc1 == "" = doc2
| otherwise = doc1
data PathWithType a b = a :|: b
data Field (pathAndType :: ( PathWithType [Symbol] * )) where
Field :: (ShowPath s) => !t -> Field (s :|: t)
deriving instance (Eq a) => Eq (Field (s:|:a))
deriving instance (Ord a) => Ord (Field (s:|:a))
instance (Show t, ShowPath s) => Show (Field (s:|:t)) where
show (Field x) =
T.unpack (showPath (Proxy @s)) ++ " =: " ++ show x ++ "\n"
instance (Semigroup t) => Semigroup (Field (s :|: t)) where
Field a <> Field b = Field $ a<>b
instance (ShowPath s, Monoid t) => Monoid (Field (s :|: t)) where
mempty = Field mempty
type PossiblyEmptyField = F.Compose PossiblyEmpty Field
peToMb :: (NamedField field, ShowPath s) => Either r (field (s:|:a)) -> Maybe a
peToMb (Left _) = Nothing
peToMb (Right f) = f L.^. rfield
peFromMb
:: (ShowPath s, NamedField field)
=> Maybe a -> Either MissingValueReason (field (s ':|: a))
peFromMb Nothing = Left NoDefault
peFromMb (Just x) = Right $ fromValue x
pattern PEField
:: ( NamedField f, ShowPath s)
=> Maybe a -> F.Compose PossiblyEmpty f (s:|:a)
pattern PEField v <- F.Compose (PE (peToMb -> v)) where
PEField v = F.Compose (PE (peFromMb v))
type DocField = F.Compose WithDoc PossiblyEmptyField
removeDoc :: F.Compose WithDoc f st -> f st
removeDoc (F.Compose (Tagged _ x)) = x
withoutDef :: F.Compose WithDoc (F.Compose PossiblyEmpty f) st
-> F.Compose WithDoc (F.Compose PossiblyEmpty f) st
withoutDef (DocField doc _) = DocField doc (Left NoDefault)
getPossiblyEmpty
:: F.Compose PossiblyEmpty f st -> Either MissingValueReason (f st)
getPossiblyEmpty (F.Compose (PE x)) = x
pattern DocField :: T.Text
-> Either MissingValueReason (g x)
-> F.Compose WithDoc (F.Compose PossiblyEmpty g) x
pattern DocField doc mbf = F.Compose (Tagged doc (F.Compose (PE mbf)))
type DocRec = Rec DocField
type family FieldTypes rs where
FieldTypes '[] = '[]
FieldTypes ((s:|:t) : rs) = t : FieldTypes rs
fieldPathList :: forall st p. (ShowPath (Fst st)) => p st -> [T.Text]
fieldPathList _ = showPathList (Proxy @(Fst st))
fieldPath :: forall st p. (ShowPath (Fst st)) => p st -> T.Text
fieldPath _ = showPath (Proxy @(Fst st))
class ShowPath p where
showPathList :: proxy p -> [T.Text]
showPath :: proxy p -> T.Text
showPath p = T.intercalate (T.pack ".") (showPathList p)
instance ShowPath '[] where
showPathList _ = []
instance (ShowPath ps, KnownSymbol p) => ShowPath (p ': ps) where
showPathList _ = T.pack (symbolVal (Proxy @p)) : showPathList (Proxy @ps)
jsonAtPath :: [T.Text] -> L.Lens' (Maybe Value) (Maybe Value)
jsonAtPath [] f x = f x
jsonAtPath (p:ps) f val = rebuild <$> recur
where
(obj, recur) = case val of
Just (Object o) -> (o, jsonAtPath ps f $ HM.lookup p o)
_ -> (HM.empty, jsonAtPath ps f $ Just $ Object HM.empty)
rebuild Nothing = Just $ Object $ HM.delete p obj
rebuild (Just v) = Just $ Object $ HM.insert p v obj
instance FromJSON (Rec PossiblyEmptyField '[]) where
parseJSON (Object _) = pure RNil
parseJSON _ = mempty
instance (FromJSON t, FromJSON (Rec PossiblyEmptyField rs), ShowPath s)
=> FromJSON (Rec PossiblyEmptyField ((s:|:t) ': rs)) where
parseJSON v = rebuild <$> parseField (L.view (jsonAtPath p) (Just v))
<*> parseJSON @(Rec PossiblyEmptyField rs) v
where p = showPathList (Proxy @s)
rebuild mbV rest = F.Compose (PE (Field <$> mbV)) :& rest
parseField mbX = if x == Null
then parsing <|> pure (Left MissingValueInJSON)
else parsing
where parsing = Right <$> parseJSON x
x = fromMaybe Null mbX
instance (RMap rs, FromJSON (Rec PossiblyEmptyField rs)) => FromJSON (Rec DocField rs) where
parseJSON v = rmap toDoc <$> parseJSON v
where toDoc = F.Compose . Tagged ""
fromJSONAs :: (FromJSON x) => x -> Value -> Result x
fromJSONAs _ = fromJSON
type family Fst a where
Fst (a:|:b) = a
type family Snd a where
Snd (a:|:b) = b
type family AllFst c p :: Constraint where
AllFst c (r ': rs) = (c (Fst r), AllFst c rs)
AllFst c '[] = ()
type family AllSnd c p :: Constraint where
AllSnd c (r ': rs) = (c (Snd r), AllSnd c rs)
AllSnd c '[] = ()
instance (ToJSON `AllSnd` rs, ShowPath `AllFst` rs)
=> ToJSON (Rec PossiblyEmptyField rs) where
toJSON RNil = Object mempty
toJSON x = into (Object mempty) x
where
into :: forall rs'. (ToJSON `AllSnd` rs', ShowPath `AllFst` rs')
=> Value -> Rec PossiblyEmptyField rs' -> Value
into o RNil = o
into o (field@(F.Compose (PE innerField)) :& fs) = into
(fromMaybe (error "Should not happen!") $
L.set (jsonAtPath $ fieldPathList field)
(Just $ case innerField of
Right (Field v) -> toJSON v
Left _ -> Null)
(Just o))
fs
instance (RMap rs, ToJSON `AllSnd` rs, ShowPath `AllFst` rs)
=> ToJSON (Rec DocField rs) where
toJSON = toJSON . rmap removeDoc
type ToJSONFields fields =
(ToJSON `AllSnd` fields, Typeable `AllSnd` fields, ShowPath `AllFst` fields)
showDocumentation
:: forall rs field. (ShowPath `AllFst` rs, Typeable `AllSnd` rs)
=> Int
-> Rec (F.Compose WithDoc field) rs
-> T.Text
showDocumentation charLimit (f :& fs) =
showF f <> case fs of
(_ :& _) -> "\n" <> showDocumentation charLimit fs
RNil -> ""
where
showF :: forall r. (ShowPath (Fst r), Typeable (Snd r))
=> F.Compose WithDoc field r -> T.Text
showF (F.Compose (Tagged doc _)) =
showPath (Proxy @(Fst r))
<> " :: " <> T.pack (cap $ show $ typeRep $ Proxy @(Snd r))
<> " : " <> doc
cap x | length x >= charLimit = take charLimit x ++ "..."
| otherwise = x
showDocumentation _ RNil = ""
class NamedField field where
rfield :: (ShowPath s) => L.Lens (field (s :|: a)) (field (s :|: b)) (Maybe a) (Maybe b)
fromValue :: (ShowPath s) => a -> field (s :|: a)
mapField :: (ShowPath s)
=> (t -> t') -> field (s:|:t) -> field (s:|:t')
mapField = L.over (rfield . L._Just)
{-# INLINE mapField #-}
(=:) :: (ShowPath s) => DocField (s :|: a) -> a -> Rec field '[ s :|: a ]
infixl 7 =:
_ =: x = fromValue x :& RNil
{-# INLINE (=:) #-}
changePath :: (ShowPath s') => field (s:|:a) -> field (s':|:a)
type family FieldDirectlyContainsTag tag field where
FieldDirectlyContainsTag tag (F.Compose (Tagged tag) f) = True
FieldDirectlyContainsTag _ _ = False
class (hasTag ~ FieldDirectlyContainsTag tag field) => FieldWithTag_ tag field hasTag where
fieldTag :: L.Traversal' (field r) tag
type FieldWithTag tag field = FieldWithTag_ tag field (FieldDirectlyContainsTag tag field)
renamedAs :: (ShowPath s', NamedField f) => proxy (s':|:a) -> f (s:|:a) -> f (s':|:a)
renamedAs _ = changePath
instance NamedField Field where
rfield f (Field v) = Field . fromM <$> f (Just v)
where fromM (Just v') = v'
fromM _ = error "Cannot remove a Field's value!"
{-# INLINE rfield #-}
fromValue = Field
changePath (Field v) = Field v
instance (NamedField f) => NamedField (F.Compose PossiblyEmpty f) where
rfield f (F.Compose (PE field)) = F.Compose . PE <$> inspect field
where
inspect (Right inner) = rebuild NoDefault (setInner inner) <$> f (L.view rfield inner)
inspect (Left r) = rebuild r fromValue <$> f Nothing
setInner inner x = L.set rfield (Just x) inner
rebuild reason _ Nothing = Left reason
rebuild _ updInner (Just x) = Right $ updInner x
fromValue = F.Compose . PE . Right . fromValue
changePath (F.Compose (PE x)) = F.Compose $ PE $ case x of
Left r -> Left r
Right f -> Right $ changePath f
instance (FieldWithTag tag g)
=> FieldWithTag_ tag (F.Compose PossiblyEmpty g) False where
fieldTag fn (F.Compose (PE (Right f))) =
F.Compose . PE . Right <$> fieldTag fn f
fieldTag _ field = pure field
class NamedFieldTag tag where
defaultTag :: tag
tagFromDoc :: T.Text -> tag
instance NamedFieldTag T.Text where
defaultTag = ""
tagFromDoc = id
instance (NamedField f, NamedFieldTag tag) => NamedField (F.Compose (Tagged tag) f) where
rfield = (\f (F.Compose (Tagged d x)) ->
F.Compose . Tagged d <$> f x) . rfield
{-# INLINE rfield #-}
fromValue = F.Compose . Tagged defaultTag . fromValue
changePath (F.Compose (Tagged d x)) = F.Compose (Tagged d (changePath x))
F.Compose (Tagged d _) =: v = F.Compose (Tagged (tagFromDoc d) (fromValue v)) :& RNil
instance FieldWithTag_ tag Field False where
fieldTag _ f = pure f
instance FieldWithTag_ tag (F.Compose (Tagged tag) f) True where
fieldTag fn (F.Compose (Tagged d x)) = rebuild <$> fn d
where
rebuild d' = F.Compose (Tagged d' x)
instance (FieldWithTag tag f, FieldDirectlyContainsTag tag (F.Compose (Tagged tag') f) ~ False)
=> FieldWithTag_ tag (F.Compose (Tagged tag') f) False where
fieldTag fn (F.Compose (Tagged t f)) = F.Compose . Tagged t <$> fieldTag fn f
class OnFields ts f1 f2 | ts f1 -> f2 where
onFields :: f1 -> f2
instance OnFields '[] a a where
onFields x = x
instance (OnFields ts f1 f2) =>
OnFields ((s:|:a) : ts) (a -> f1) (Field (s:|:a) -> f2) where
onFields f (Field x) = onFields @ts (f x)
runcurryF :: forall ts f1 f a. (OnFields ts f1 (CurriedF f ts a))
=> f1 -> Rec f ts -> a
runcurryF = runcurry . onFields @ts
runcurryAF :: forall ts f1 f g a. (Applicative f, OnFields ts f1 (CurriedF g ts a))
=> f1 -> Rec (F.Compose f g) ts -> f a
runcurryAF = runcurryA . onFields @ts
type family RIndex' r1 rs1 (r :: k) (rs :: [k]) :: Nat where
RIndex' r1 rs1 r (r ': rs) = 'Z
RIndex' r1 rs1 r (notR ': rs) = 'S (RIndex' r1 rs1 r rs)
RIndex' r1 rs1 r rs = TypeError
(Text "Field " :<>: ShowType r1 :<>: Text " is not present in record " :<>: ShowType rs1)
class (RElem f rs (RIndex' f rs f rs)) => rs `HasField` f
instance (RElem f (r ': rs) (RIndex' f (r ': rs) f (r ': rs))) => (r ': rs) `HasField` f
type family RImage' rs1 ss1 (rs :: [k]) (ss :: [k]) :: [Nat] where
RImage' rs1 ss1 '[] ss = '[]
RImage' rs1 ss1 (r ': rs) ss = RIndex' r ss r ss ': RImage' rs1 ss1 rs ss
class (RSubset rs ss (RImage' rs ss rs ss)) => ss `Includes` rs
instance ss `Includes` '[]
instance (RSubset (r ': rs) ss (RImage' (r ': rs) ss (r ': rs) ss)) => ss `Includes` (r ': rs)
rsubset :: (Functor g, ss `Includes` rs) => (Rec f rs -> g (Rec f rs)) -> Rec f ss -> g (Rec f ss)
rsubset = VL.rsubset
rcast :: (ss `Includes` rs) => Rec f ss -> Rec f rs
rcast = VL.rcast
rreplace :: (ss `Includes` rs) => Rec f rs -> Rec f ss -> Rec f ss
rreplace = VL.rreplace
type rs `EquivalentTo` ss = (rs `Includes` ss, ss `Includes` rs)
fld :: forall s a rs field proxy.
(NamedField field, rs `HasField` (s:|:a), ShowPath s)
=> proxy (s:|:a)
-> L.Lens' (Rec field rs) (Maybe a)
fld _ = VL.rlens @(s:|:a) . rfield
(^^.) :: (NamedField field, rs `HasField` (s:|:t), ShowPath s, Monoid t)
=> Rec field rs -> proxy (s:|:t) -> t
record ^^. field = record L.^. fld field . L._Just
infixl 8 ^^.
(^^?) :: (NamedField field, rs `HasField` (s:|:t), ShowPath s)
=> Rec field rs -> proxy (s:|:t) -> (Maybe t)
record ^^? field = record L.^. fld field
infixl 8 ^^?
(^^?!) :: (NamedField field, rs `HasField` (s:|:t), ShowPath s)
=> Rec field rs -> proxy (s:|:t) -> t
record ^^?! field = record L.^?! fld field . L._Just
infixl 8 ^^?!
(%%~) :: (NamedField field, rs `HasField` (s:|:t), ShowPath s)
=> proxy (s:|:t) -> (t -> t) -> Rec field rs -> Rec field rs
field %%~ f = fld field . L._Just L.%~ f
infixr 4 %%~
(..~) :: (NamedField field, rs `HasField` (s:|:t), ShowPath s)
=> proxy (s:|:t) -> t -> Rec field rs -> Rec field rs
field ..~ v = fld field L..~ Just v
infixr 4 ..~
singleton :: (NamedField f) => t -> Rec f '[ ('[]:|:t ) ]
singleton x = fromValue x :& RNil
useDef :: (NamedField f, ShowPath s) => DocField (s:|:t) -> Rec f '[ (s:|:t) ]
useDef f = f =: (f L.^?! rfield . L._Just)
docField :: forall s t. (KnownSymbol s) => t -> T.Text -> DocField ('[s]:|:t)
docField defVal doc = DocField doc $ Right $ Field defVal
itmLevel
:: forall s rs. (KnownSymbol s)
=> T.Text -> DocRec rs -> IntermediaryLevel '[s] rs
itmLevel doc content = ItmLvl $ DocField doc $ Right $ Field content
fieldFromDef
:: forall s t. (KnownSymbol s, Default t)
=> T.Text -> DocField ('[s]:|:t)
fieldFromDef = docField def
fieldNoDef :: forall s t. T.Text -> DocField ('[s]:|:t)
fieldNoDef doc = DocField doc $ Left NoDefault
type family DeleteIn a b where
DeleteIn t (t ': ts) = DeleteIn t ts
DeleteIn t (t' ': ts) = t' : DeleteIn t ts
DeleteIn t '[] = '[]
type family Difference a b where
Difference ts' (t ': ts) = Difference (DeleteIn t ts') ts
Difference ts' '[] = ts'
rcastAs
:: (rs `Includes` selected)
=> p selected -> Rec f rs -> Rec f selected
rcastAs _ r = rcast r
rsplit
:: (rs `Includes` selected, rs `Includes` (rs `Difference` selected))
=> Rec f rs -> (Rec f selected, Rec f (rs `Difference` selected))
rsplit r = (rcast r, rcast r)
rsplitFrom
:: (rs `Includes` selected, rs `Includes` (rs `Difference` selected))
=> p selected -> Rec f rs -> (Rec f selected, Rec f (rs `Difference` selected))
rsplitFrom _ r = rsplit r
rdifference
:: (rs `Includes` selected, rs `Includes` (rs `Difference` selected))
=> Rec f rs -> p selected -> Rec f (rs `Difference` selected)
rdifference r _ = rcast r
type a `Intersection` b = a `Difference` (a `Difference` b)
rintersection
:: (a `Includes` (a `Difference` b)
,a `Includes` (a `Intersection` b)
,b `Includes` (b `Intersection` a)
,b `Includes` (b `Difference` a))
=> Rec f a -> Rec f b -> ( Rec f (a `Difference` b), Rec f (a `Intersection` b)
, Rec f (b `Intersection` a), Rec f (b `Difference` a) )
rintersection a b = (rcast a, rcast a, rcast b, rcast b)
class PrefixPath (s::[Symbol]) rs where
type s `PrefixingAll` rs :: [PathWithType [Symbol] *]
prefixPath :: (NamedField f) => Rec f rs -> Rec f (s `PrefixingAll` rs)
instance PrefixPath s '[] where
type s `PrefixingAll` '[] = '[]
prefixPath _ = RNil
instance (PrefixPath s ps, ShowPath (s++p1)) => PrefixPath s ( (p1:|:t) : ps) where
type s `PrefixingAll` ( (p1:|:t) : ps) = ( (s++p1:|:t) : s `PrefixingAll` ps)
prefixPath (f :& fs) = (changePath f) :& prefixPath @s fs
data MD = MD
instance Eq MD where
_ == _ = True
instance Ord MD where
compare _ _ = EQ
instance ToJSON MD where
toJSON _ = Null
instance FromJSON MD where
parseJSON _ = pure MD
instance Show MD where
show _ = "<just_doc>"
newtype IntermediaryLevel_ a = ItmLvl (DocField a)
type IntermediaryLevel s rs = IntermediaryLevel_ (s:|:DocRec rs)
type FlattenedLevel s rs = s `PrefixingAll` rs
rinclude
:: forall s rs. (PrefixPath s rs, ShowPath s)
=> IntermediaryLevel s rs -> DocRec (FlattenedLevel s rs)
rinclude (ItmLvl (DocField _ (Left r))) =
error $ "rinclude: Trying to flatten an empty field (" ++ show r ++ ")!"
rinclude (ItmLvl (DocField _doc (Right (Field r)))) = prefixPath @s r
class ComposableNesting f lvl2 where
(-.) :: (NestedLvlConstraints rs f p lvl2, ShowPath (s++p))
=> IntermediaryLevel s rs -> f (p:|:lvl2) -> NestedLvl s f p lvl2
infixr 9 -.
type family NestedLvl s f p lvl2 where
NestedLvl s IntermediaryLevel_ p (DocRec rs') = IntermediaryLevel (s++p) rs'
NestedLvl s f p t = f ((s++p):|:t)
type family NestedLvlConstraints rs f p lvl2 :: Constraint where
NestedLvlConstraints rs IntermediaryLevel_ p (DocRec rs') =
( rs `Includes` (p `PrefixingAll` rs') )
NestedLvlConstraints rs f p t =
( rs `HasField` (p:|:t) )
instance ComposableNesting IntermediaryLevel_ (DocRec rs') where
_ -. ItmLvl f = ItmLvl $ changePath f
instance (NamedField f) => ComposableNesting f t where
_ -. f = changePath f
funder :: forall s p t. ( ShowPath (s ': p) )
=> DocField (p:|:t) -> DocField ((s ': p) :|: t)
funder = changePath
(-/) :: forall s rs selected f.
(rs `Includes` selected, PrefixPath s selected, NamedField f)
=> IntermediaryLevel s rs -> Rec f selected -> Rec f (s `PrefixingAll` selected)
_ -/ r = prefixPath @s r
infixr 6 -/
runder :: forall s selected f. (PrefixPath '[s] selected, NamedField f)
=> Rec f selected -> Rec f ('[s] `PrefixingAll` selected)
runder r = prefixPath @'[s] r
type family Strip s src where
Strip (a ': as) (a ': bs) = Strip as bs
Strip (a ': as) (b ': bs) = b ': bs
Strip as bs = bs
class UnprefixPath (s::[Symbol]) rs where
type s `UnprefixingAll` rs :: [PathWithType [Symbol] *]
unprefixPath :: (NamedField f) => Rec f rs -> Rec f (s `UnprefixingAll` rs)
instance UnprefixPath s '[] where
type s `UnprefixingAll` '[] = '[]
unprefixPath _ = RNil
instance (UnprefixPath s ps, ShowPath (Strip s p1))
=> UnprefixPath s ( (p1:|:t) : ps) where
type s `UnprefixingAll` ( (p1:|:t) : ps) =
( (Strip s p1 :|: t) : s `UnprefixingAll` ps)
unprefixPath (f :& fs) = changePath f :& unprefixPath @s fs
rdrill :: forall s inner outer f.
( inner ~ (s `UnprefixingAll` (s `PrefixingAll` inner))
, UnprefixPath s (s `PrefixingAll` inner)
, outer `Includes` (s `PrefixingAll` inner)
, NamedField f )
=> IntermediaryLevel s inner
-> Rec f outer
-> Rec f inner
rdrill _ outer = unprefixPath @s (rcast outer :: Rec f (s `PrefixingAll` inner))
rsplitDrill :: forall s inner outer f.
( inner ~ (s `UnprefixingAll` (s `PrefixingAll` inner))
, UnprefixPath s (s `PrefixingAll` inner)
, outer `Includes` (s `PrefixingAll` inner)
, outer `Includes` (outer `Difference` (FlattenedLevel s inner))
, NamedField f )
=> IntermediaryLevel s inner
-> Rec f outer
-> (Rec f inner, Rec f (outer `Difference` (FlattenedLevel s inner)))
rsplitDrill il outer = (rdrill il outer, rcast outer)
rfoldSubset
:: forall outer' inner outer p t proxy f.
( outer `Includes` inner
, ((p:|:t) ': outer) `Includes` outer')
=> proxy inner
-> (Rec f inner -> f (p:|:t))
-> Rec f outer
-> Rec f outer'
rfoldSubset _ f r = rcast $ f (rcast r) :& r
withSameFields :: Rec f rs -> Rec g rs -> t -> t
withSameFields _ _ x = x
type IdentityField = F.Identity
(&:) :: t -> Rec IdentityField ts -> Rec IdentityField (t ': ts)
x &: r = F.Identity x :& r
infixr 5 &:
class ApplyRec fns fields results | fns fields -> results where
appRec :: (NamedField f) => Rec F.Identity fns -> Rec f fields -> Rec f results
instance ApplyRec '[] a a where
appRec RNil r = r
instance (ApplyRec fns fields results, ShowPath s)
=> ApplyRec ( (a -> b) : fns ) ((s:|:a) : fields) ((s:|:b) : results) where
appRec (F.Identity f :& fns) (field :& fields) =
L.over (rfield . L._Just) f field :& appRec fns fields
type family FirstFieldSkipped rs where
FirstFieldSkipped ((s:|:MD) : rs) = 'True
FirstFieldSkipped a = 'False
class (skipFirst ~ FirstFieldSkipped rs)
=> BuildRecFrom f rs (acc::[PathWithType [Symbol] *]) skipFirst where
type RecCtor f rs acc skipFirst
buildRecFrom_ :: Rec f acc -> DocRec rs -> RecCtor f rs acc skipFirst
instance BuildRecFrom f '[] acc 'False where
type RecCtor f '[] acc 'False = Rec f acc
buildRecFrom_ acc RNil = acc
{-# INLINE buildRecFrom_ #-}
instance ( BuildRecFrom f rs (acc ++ '[s:|:a]) (FirstFieldSkipped rs)
, FirstFieldSkipped ((s:|:a):rs) ~ 'False
, NamedField f, ShowPath s )
=> BuildRecFrom f ((s:|:a) : rs) acc 'False where
type RecCtor f ((s:|:a) : rs) acc 'False =
a -> RecCtor f rs (acc ++ '[s:|:a]) (FirstFieldSkipped rs)
buildRecFrom_ acc (r :& rs) a = buildRecFrom_ (acc <+> r =: a) rs
{-# INLINE buildRecFrom_ #-}
instance ( BuildRecFrom f rs (acc++'[s:|:MD]) (FirstFieldSkipped rs)
, NamedField f, ShowPath s )
=> BuildRecFrom f ((s:|:MD) : rs) acc 'True where
type RecCtor f ((s:|:MD) : rs) acc 'True =
RecCtor f rs (acc ++ '[s:|:MD]) (FirstFieldSkipped rs)
buildRecFrom_ acc (_ :& rs) = buildRecFrom_ (acc <+> fromValue @f @s MD :& RNil) rs
{-# INLINE buildRecFrom_ #-}
recFrom :: forall f rs. (BuildRecFrom f rs '[] (FirstFieldSkipped rs))
=> DocRec rs -> RecCtor f rs '[] (FirstFieldSkipped rs)
recFrom = buildRecFrom_ @f RNil
{-# INLINE recFrom #-}
data RecBijection f as bs = RecBijection
{ applyRecBij :: Rec f as -> Rec f bs
, applyRecBijInv :: Rec f bs -> Rec f as
}
instance Cat.Category (RecBijection f) where
id = RecBijection id id
RecBijection f fi . RecBijection g gi = RecBijection (f . g) (gi . fi)
invertRecBij :: RecBijection f as bs -> RecBijection f bs as
invertRecBij (RecBijection f g) = RecBijection g f
(<<|>>)
:: ( as `Intersection` as' ~ '[]
, bs `Intersection` bs' ~ '[]
, (as ++ as') `Includes` as, (as ++ as') `Includes` as'
, (bs ++ bs') `Includes` bs, (bs ++ bs') `Includes` bs')
=> RecBijection f as bs
-> RecBijection f as' bs'
-> RecBijection f (as++as') (bs++bs')
RecBijection f fi <<|>> RecBijection g gi =
RecBijection (\r -> f (rcast r) <+> g (rcast r))
(\r -> fi (rcast r) <+> gi (rcast r))
bijectField :: forall s f a b. (ShowPath s, NamedField f)
=> (a -> b) -> (b -> a) -> RecBijection f '[s:|:a] '[s:|:b]
bijectField f g = RecBijection (\(fl :& RNil) -> mapField f fl :& RNil)
(\(fl :& RNil) -> mapField g fl :& RNil)
{-# INLINE bijectField #-}
bijectField' :: forall s s' f a b. (ShowPath s, ShowPath s', NamedField f)
=> (a -> b) -> (b -> a) -> RecBijection f '[s:|:a] '[s':|:b]
bijectField' f g = RecBijection (\(fl :& RNil) -> changePath (mapField f fl) :& RNil)
(\(fl :& RNil) -> changePath (mapField g fl) :& RNil)
{-# INLINE bijectField' #-}
renameField :: forall s s' f a. (ShowPath s, ShowPath s', NamedField f)
=> RecBijection f '[s:|:a] '[s':|:a]
renameField = RecBijection (\(fl :& RNil) -> changePath fl :& RNil)
(\(fl :& RNil) -> changePath fl :& RNil)
{-# INLINE renameField #-}
addConstField :: forall s f a. f (s:|:a) -> RecBijection f '[] '[s:|:a]
addConstField x = RecBijection (\_ -> x :& RNil) (const RNil)
{-# INLINE addConstField #-}
bijectUnder
:: forall s f as bs.
( as ~ UnprefixingAll s (PrefixingAll s as)
, bs ~ UnprefixingAll s (PrefixingAll s bs)
, PrefixPath s as, PrefixPath s bs, NamedField f
, UnprefixPath s (PrefixingAll s as)
, UnprefixPath s (PrefixingAll s bs) )
=> RecBijection f as bs
-> RecBijection f (s `PrefixingAll` as) (s `PrefixingAll` bs)
bijectUnder (RecBijection f fi) =
RecBijection (prefixPath @s . f . unprefixPath @s)
(prefixPath @s . fi . unprefixPath @s)