generic-data-surgery 0.2.0.0 → 0.2.1.0
raw patch · 5 files changed
+165/−8 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) (f :: k -> *). (Fcf.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GInsertConstr (n GHC.TypeNats.- 1) g), Fcf.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GInsertConstr n (f GHC.Generics.:+: g)
- Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) (f :: k -> *). (Fcf.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GRemoveConstr (n GHC.TypeNats.- 1) g), Fcf.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GRemoveConstr n (f GHC.Generics.:+: g)
- Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) s (m :: GHC.Generics.Meta) i t. (Fcf.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GInsertField (n GHC.TypeNats.- 1) g), Fcf.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GInsertField n (GHC.Generics.M1 s m (GHC.Generics.K1 i t) GHC.Generics.:*: g)
- Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) s (m :: GHC.Generics.Meta) i t. (Fcf.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GRemoveField (n GHC.TypeNats.- 1) g), Fcf.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GRemoveField n (GHC.Generics.M1 s m (GHC.Generics.K1 i t) GHC.Generics.:*: g)
+ Generic.Data.Surgery: fromORLazy :: forall a l x. (Generic a, FromORRepLazy a l) => OR l x -> a
+ Generic.Data.Surgery: toORLazy :: forall a l x. (Generic a, ToORRepLazy a l) => a -> OR l x
+ Generic.Data.Surgery: type FromORLazy f l = (FromOR (Lazify f) l, Coercible (Arborify l) f)
+ Generic.Data.Surgery: type FromORRepLazy a l = FromORLazy (Rep a) l
+ Generic.Data.Surgery: type OROfLazy a = OR (Linearize (Lazify (Rep a))) ()
+ Generic.Data.Surgery: type ToORLazy f l = (ToOR (Lazify f) l, Coercible f (Arborify l))
+ Generic.Data.Surgery: type ToORRepLazy a l = ToORLazy (Rep a) l
+ Generic.Data.Surgery.Internal: fromORLazy :: forall a l x. (Generic a, FromORRepLazy a l) => OR l x -> a
+ Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) (f :: k -> *). (Data.Type.Bool.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GInsertConstr (n GHC.TypeNats.- 1) g), Fcf.Utils.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GInsertConstr n (f GHC.Generics.:+: g)
+ Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) (f :: k -> *). (Data.Type.Bool.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GRemoveConstr (n GHC.TypeNats.- 1) g), Fcf.Utils.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GRemoveConstr n (f GHC.Generics.:+: g)
+ Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) s (m :: GHC.Generics.Meta) i t. (Data.Type.Bool.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GInsertField (n GHC.TypeNats.- 1) g), Fcf.Utils.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GInsertField n (GHC.Generics.M1 s m (GHC.Generics.K1 i t) GHC.Generics.:*: g)
+ Generic.Data.Surgery.Internal: instance forall k (n :: GHC.Types.Nat) (g :: k -> *) s (m :: GHC.Generics.Meta) i t. (Data.Type.Bool.If (n Data.Type.Equality.== 0) (() :: Constraint) (Generic.Data.Surgery.Internal.GRemoveField (n GHC.TypeNats.- 1) g), Fcf.Utils.IsBool (n Data.Type.Equality.== 0)) => Generic.Data.Surgery.Internal.GRemoveField n (GHC.Generics.M1 s m (GHC.Generics.K1 i t) GHC.Generics.:*: g)
+ Generic.Data.Surgery.Internal: toORLazy :: forall a l x. (Generic a, ToORRepLazy a l) => a -> OR l x
+ Generic.Data.Surgery.Internal: type FromORLazy f l = (FromOR (Lazify f) l, Coercible (Arborify l) f)
+ Generic.Data.Surgery.Internal: type FromORRepLazy a l = FromORLazy (Rep a) l
+ Generic.Data.Surgery.Internal: type OROfLazy a = OR (Linearize (Lazify (Rep a))) ()
+ Generic.Data.Surgery.Internal: type ToORLazy f l = (ToOR (Lazify f) l, Coercible f (Arborify l))
+ Generic.Data.Surgery.Internal: type ToORRepLazy a l = ToORLazy (Rep a) l
Files
- CHANGELOG.md +5/−0
- generic-data-surgery.cabal +1/−1
- src/Generic/Data/Surgery.hs +15/−6
- src/Generic/Data/Surgery/Internal.hs +122/−0
- test/surgery.hs +22/−1
CHANGELOG.md view
@@ -1,3 +1,8 @@+# 0.2.1.0++- Add `toORLazy` and `fromORLazy`, to clean up data types with strictness+ annotations. (Thanks to blmage.)+ # 0.2.0.0 - Compatibility with generic-data 0.4.0.0
generic-data-surgery.cabal view
@@ -1,5 +1,5 @@ name: generic-data-surgery-version: 0.2.0.0+version: 0.2.1.0 synopsis: Surgery for generic data types description: Transform data types before passing them to generic functions.
src/Generic/Data/Surgery.hs view
@@ -4,9 +4,9 @@ -- Functions in this module are expected to be used with visible type -- applications. Surgeries have a lot of type parameters, but usually only the -- first one to three type arguments need to be passed via @TypeApplications@.--- Functions are annotated with \"functional dependencies\", with a meaning--- similar to the homonymous GHC extension for type classes (click on--- \"Details\" under each function to see those).+-- Functions are documented with informal \"functional dependencies\",+-- clarifying which type parameters can be inferred from which others+-- (click on \"Details\" under each function to see those). -- -- Remember that not all parameters to the left of a functional dependency -- arrow need to be annotated explicitly to determine those on the right. Some@@ -21,9 +21,9 @@ -- has a "Generic.Data.Microsurgery" module (since 0.4.0.0) to modify some -- metadata of generic representations. --- -- See also the documentation in that module about surgeries using- -- <https://hackage.haskell.org/package/generic-data generic-lens>,- -- when you want to /update/ fields, rather than remove or insert them.+ -- If you only want to /update/ fields, rather than remove or insert them,+ -- see also the documentation in the above module, on making surgeries out of+ -- <https://hackage.haskell.org/package/generic-data generic-lens>. -- * Synthetic data types @@ -44,6 +44,11 @@ , OROf + , toORLazy+ , fromORLazy++ , OROfLazy+ -- ** Unnamed fields , removeCField , insertCField@@ -87,8 +92,12 @@ , ToORRep , ToOR+ , ToORRepLazy+ , ToORLazy , FromORRep , FromOR+ , FromORRepLazy+ , FromORLazy -- ** Surgeries
src/Generic/Data/Surgery/Internal.hs view
@@ -48,12 +48,33 @@ -- -- @x@ corresponds to the last parameter of 'Rep', and is currently ignored by -- this module (no support for 'Generic1').+--+-- === General sketch+--+-- >+-- > toOR surgeries fromOR'+-- > data MyType --------> OR (Rep MyType) ----------> OR alteredRep ---------> Data alteredRep+-- > |+-- > | myGenericFun :: Generic a => a -> a+-- > fromOR surgeries toOR' v+-- > data MyType <-------- OR (Rep MyType) <---------- OR alteredRep <--------- Data alteredRep+-- >+--+-- If instead @myGenericFun@ is only a consumer of @a@ (resp. producer),+-- then you only need the top half of the diagram (resp. bottom half).+-- For example, in aeson:+-- @genericToJSON@ (consumer), @genericParseJSON@ (producer). newtype OR (l :: k -> Type) (x :: k) = OR { unOR :: l x } -- | /Move fresh data to the Operating Room, where surgeries can be applied./ -- -- Convert a generic type to a generic representation. --+-- When inserting or removing fields, there may be a mismatch with strict/unpacked fields.+-- To work around this, you can switch to 'toORLazy', if your operations don't care about+-- dealing with a normalized 'Rep' (in which all the strictness annotations have been+-- replaced with lazy defaults).+-- -- === __Details__ -- -- ==== Type parameters@@ -72,6 +93,32 @@ toOR :: forall a l x. (Generic a, ToORRep a l) => a -> OR l x toOR = OR . gLinearize . from +-- | /Move normalized data to the Operating Room, where surgeries can be applied./+--+-- Convert a generic type to a generic representation, in which all the strictness+-- annotations have been normalized to lazy defaults.+--+-- This variant is useful when one needs to operate on fields whose 'Rep' has different+-- strictness annotations than the ones used by 'DefaultMetaSel'.+--+-- === __Details__+--+-- ==== Type parameters+--+-- @+-- a :: 'Type' -- Generic type+-- l :: k -> 'Type' -- Generic representation (simplified and normalized)+-- x :: k -- Ignored+-- @+--+-- ==== Functional dependencies+--+-- @+-- a -> l+-- @+toORLazy :: forall a l x. (Generic a, ToORRepLazy a l) => a -> OR l x+toORLazy = OR . gLinearize @(Arborify l) . coerce' . from+ -- | /Move altered data out of the Operating Room, to be consumed by/ -- /some generic function./ --@@ -142,6 +189,11 @@ -- 'fromOR' \@a -- with TypeApplications -- @ --+-- When inserting or removing fields, there may be a mismatch with strict/unpacked fields.+-- To work around this, you can switch to 'fromORLazy', if your operations don't care+-- about dealing with a normalized 'Rep' (in which all the strictness annotations have+-- been replaced with lazy defaults).+-- -- === __Details__ -- -- ==== Type parameters@@ -160,10 +212,46 @@ fromOR :: forall a l x. (Generic a, FromORRep a l) => OR l x -> a fromOR = to . gArborify . unOR +-- | /Move normalized data out of the Operating Room and back to the real/+-- /world./+--+-- The inverse of 'toORLazy'.+--+-- It may be useful to annotate the output type of 'fromORLazy',+-- since the rest of the type depends on it and the only way to infer it+-- otherwise is from the context. The following annotations are possible:+--+-- @+-- 'fromORLazy' :: 'OROfLazy' a -> a+-- 'fromORLazy' \@a -- with TypeApplications+-- @+--+-- === __Details__+--+-- ==== Type parameters+--+-- @+-- a :: 'Type' -- Generic type+-- l :: k -> 'Type' -- Generic representation (simplified and normalized)+-- x :: k -- Ignored+-- @+--+-- ==== Functional dependencies+--+-- @+-- a -> l+-- @+fromORLazy :: forall a l x. (Generic a, FromORRepLazy a l) => OR l x -> a+fromORLazy = to . coerce' . gArborify @(Lazify (Rep a)) . unOR+ -- | The simplified generic representation type of type @a@, -- that 'toOR' and 'fromOR' convert to and from. type OROf a = OR (Linearize (Rep a)) () +-- | The simplified and normalized generic representation type of type @a@,+-- that 'toORLazy' and 'fromORLazy' convert to and from.+type OROfLazy a = OR (Linearize (Lazify (Rep a))) ()+ -- | This constraint means that @a@ is convertible /to/ its simplified -- generic representation. Implies @'OROf' a ~ 'OR' l ()@. type ToORRep a l = ToOR (Rep a) l@@ -180,6 +268,26 @@ -- generic representation of @a@ directly, @f ~ 'Rep' a@. type FromOR f l = (GArborify f, Linearize f ~ l, f ~ Arborify l) +-- | This constraint means that @a@ is convertible /to/ its simplified+-- and normalized generic representation (i.e., with all its strictness+-- annotations normalized to lazy defaults).+-- Implies @'OROfLazy' a ~ 'OR' l ()@.+type ToORRepLazy a l = ToORLazy (Rep a) l++-- | This constraint means that @a@ is convertible /from/ its simplified+-- and normalized generic representation (i.e., with all its strictness+-- annotations normalized to lazy defaults).+-- Implies @'OROfLazy' a ~ 'OR' l ()@.+type FromORRepLazy a l = FromORLazy (Rep a) l++-- | Similar to 'FromLazyORRep', but as a constraint on the standard+-- generic representation of @a@ directly, @f ~ 'Rep' a@.+type FromORLazy f l = (FromOR (Lazify f) l, Coercible (Arborify l) f)++-- | Similar to 'ToORRepLazy', but as a constraint on the standard+-- generic representation of @a@ directly, @f ~ 'Rep' a@.+type ToORLazy f l = (ToOR (Lazify f) l, Coercible f (Arborify l))+ -- -- | @'removeCField' \@n \@t@: remove the @n@-th field, of type @t@, in a@@ -865,6 +973,20 @@ <=< Bimap (arb nDiv2) (arb (n-nDiv2)) <=< SplitAt nDiv2 ) (op f g)++type family Lazify (f :: k -> *) :: k -> *+type instance Lazify (M1 i m f) = M1 i (LazifyMeta m) (Lazify f)+type instance Lazify (f :*: g) = Lazify f :*: Lazify g+type instance Lazify (f :+: g) = Lazify f :+: Lazify g+type instance Lazify (K1 i c) = K1 i c+type instance Lazify U1 = U1+type instance Lazify V1 = V1++type family LazifyMeta (m :: Meta) :: Meta+type instance LazifyMeta ('MetaData n m p nt) = 'MetaData n m p nt+type instance LazifyMeta ('MetaCons n f s) = 'MetaCons n f s+type instance LazifyMeta ('MetaSel mn su ss ds)+ = 'MetaSel mn 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy data SplitAt :: Nat -> (k -> *) -> (k -> *, k -> *) -> * type instance Eval (SplitAt n (f :+: g)) =
test/surgery.hs view
@@ -25,6 +25,7 @@ data P = P Int Int Int deriving (Eq, Show, Generic) data R = R { u, v, w :: Int } deriving (Eq, Show, Generic)+data S = S { u' :: Int, v' :: !Int, w' :: {-# UNPACK #-} !Int } deriving (Eq, Show, Generic) main :: IO () main = defaultMain test@@ -56,6 +57,10 @@ rt (R 1 2 3) (fromOR . insertRField @"u" . removeRField @"u" . toOR) , testCase "RField-ins-rmv" $ rt ((), R 1 2 3) (fmap fromOR . removeRField @"t" . insertRField @"t" @1 . fmap toOR)+ , testCase "SField-rmv-ins" $+ rt (S 1 2 3) (fromORLazy . insertRField @"u'" . removeRField @"u'" . toORLazy)+ , testCase "SField-ins-rmv" $+ rt ((), S 1 2 3) (fmap fromORLazy . removeRField @"t" . insertRField @"t" @1 . fmap toORLazy) -- Type error on 8.0 #if __GLASGOW_HASKELL__ >= 802 , testCase "Constr-rmv-ins" $@@ -76,6 +81,10 @@ "R {u = 1, w = 3}" @?= (show' . fromOR' . snd . removeRField @"v" . toOR) (R 1 2 3) + , testCase "removeSField" $+ "S {u' = 1, w' = 3}" @?=+ (show' . fromOR' . snd . removeRField @"v'" . toORLazy) (S 1 2 3)+ , testCase "insertCField" $ "P 1 () 2 3" @?= (show' . fromOR' . insertCField' @1 () . toOR) (P 1 2 3)@@ -84,6 +93,10 @@ "R {u = 1, n = (), v = 2, w = 3}" @?= (show' . fromOR' . insertRField' @"n" @1 () . toOR) (R 1 2 3) + , testCase "insertSField" $+ "S {u' = 1, n' = (), v' = 2, w' = 3}" @?=+ (show' . fromOR' . insertRField' @"n'" @1 () . toORLazy) (S 1 2 3)+ -- Loops on 8.0 #if __GLASGOW_HASKELL__ >= 802 -- N.B. Identity (for constructor B) is inferred.@@ -107,13 +120,21 @@ R 0 0 0 @?= (fromOR . snd . removeRField @"v" @1 @[Int] . toOR') def + , testCase "removeSField" $+ S 0 0 0 @?=+ (fromORLazy . snd . removeRField @"v'" @1 @[Int] . toOR') def+ , testCase "insertCField" $ P 0 9 0 @?= (fromOR . insertCField' @1 9 . toOR') def - , testCase "insertCField" $+ , testCase "insertRField" $ R 0 9 0 @?= (fromOR . insertRField' @"v" 9 . toOR') def++ , testCase "insertSField" $+ S 0 9 0 @?=+ (fromORLazy . insertRField' @"v'" 9 . toOR') def , testCase "removeConstr" $ Right A @?=