generic-data-functions 0.1.1 → 0.2.0
raw patch · 15 files changed
+264/−116 lines, 15 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Generic.Data.Function.Error: type ENoEmpty = 'Text "Requested generic instance disallows empty data type"
- Generic.Data.Function.Error: type EUnexpectedNonSum = 'Text "Refusing to derive sum generic instance for non-sum data type"
- Generic.Data.Function.Error: type EUnexpectedSum = 'Text "Cannot derive non-sum generic instance for sum data type"
- Generic.Data.Function.Example: D1 :: a -> D a
- Generic.Data.Function.Example: D2 :: a -> a -> D a
- Generic.Data.Function.Example: data D a
- Generic.Data.Function.Example: instance GHC.Base.Monoid a => GHC.Base.Monoid (Generic.Data.Function.Example.Showly a)
- Generic.Data.Function.Example: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Generic.Data.Function.Example.Showly a)
- Generic.Data.Function.Example: instance GHC.Generics.Generic (Generic.Data.Function.Example.D a)
- Generic.Data.Function.Example: instance GHC.Show.Show a => GHC.Show.Show (Generic.Data.Function.Example.D a)
- Generic.Data.Function.Example: instance GHC.Show.Show a => GHC.Show.Show (Generic.Data.Function.Example.Showly a)
- Generic.Data.Function.Example: instance Generic.Data.Function.FoldMap.Constructor.GenericFoldMap (Generic.Data.Function.Example.Showly GHC.Base.String)
- Generic.Data.Function.FoldMap.NonSum: instance (TypeError ...) => Generic.Data.Function.FoldMap.NonSum.GFoldMapNonSum m GHC.Generics.V1
- Generic.Data.Function.FoldMap.NonSum: instance forall k m (f :: k -> Type) (c :: GHC.Generics.Meta). Generic.Data.Function.FoldMap.NonSum.GFoldMapNonSum m f => Generic.Data.Function.FoldMap.NonSum.GFoldMapNonSum m (GHC.Generics.D1 c f)
- Generic.Data.Function.FoldMap.NonSum: instance forall k m (l :: k -> Type) (r :: k -> Type). (TypeError ...) => Generic.Data.Function.FoldMap.NonSum.GFoldMapNonSum m (l GHC.Generics.:+: r)
- Generic.Data.Function.FoldMap.Sum: instance (TypeError ...) => Generic.Data.Function.FoldMap.Sum.GFoldMapSum m GHC.Generics.V1
- Generic.Data.Function.FoldMap.Sum: instance forall k m (c :: GHC.Generics.Meta) (f :: k -> Type). (TypeError ...) => Generic.Data.Function.FoldMap.Sum.GFoldMapSum m (GHC.Generics.C1 c f)
- Generic.Data.Function.FoldMap.Sum: instance forall k m (f :: k -> Type) (c :: GHC.Generics.Meta). Generic.Data.Function.FoldMap.Sum.GFoldMapSum m f => Generic.Data.Function.FoldMap.Sum.GFoldMapSum m (GHC.Generics.D1 c f)
- Generic.Data.Function.FoldMap.Sum: instance forall k m (l :: k -> Type) (r :: k -> Type). Generic.Data.Function.FoldMap.Sum.GFoldMapCSum m (l GHC.Generics.:+: r) => Generic.Data.Function.FoldMap.Sum.GFoldMapSum m (l GHC.Generics.:+: r)
- Generic.Data.Function.FoldMap.SumConsByte: instance (TypeError ...) => Generic.Data.Function.FoldMap.SumConsByte.GFoldMapSumConsByte m GHC.Generics.V1
- Generic.Data.Function.FoldMap.SumConsByte: instance forall k m (c :: GHC.Generics.Meta) (f :: k -> Type). (TypeError ...) => Generic.Data.Function.FoldMap.SumConsByte.GFoldMapSumConsByte m (GHC.Generics.C1 c f)
- Generic.Data.Function.Traverse.NonSum: class GTraverseNonSum' cd f f'
- Generic.Data.Function.Traverse.NonSum: gTraverseNonSum' :: GTraverseNonSum' cd f f' => f (f' p)
- Generic.Data.Function.Traverse.NonSum: instance forall k (f :: Type -> Type) (cd :: GHC.Generics.Meta) (f' :: k -> Type). (GHC.Base.Functor f, Generic.Data.Function.Traverse.NonSum.GTraverseNonSum' cd f f') => Generic.Data.Function.Traverse.NonSum.GTraverseNonSum f (GHC.Generics.D1 cd f')
- Generic.Data.Function.Traverse.NonSum: instance forall k1 k2 (cd :: k1) (f :: Type -> Type) (l :: k2 -> Type) (r :: k2 -> Type). (TypeError ...) => Generic.Data.Function.Traverse.NonSum.GTraverseNonSum' cd f (l GHC.Generics.:+: r)
- Generic.Data.Function.Traverse.NonSum: instance forall k1 k2 (cd :: k1) (f :: Type -> Type). (TypeError ...) => Generic.Data.Function.Traverse.NonSum.GTraverseNonSum' cd f GHC.Generics.V1
- Generic.Data.Function.Traverse.NonSum: instance forall k1 k2 (f :: Type -> Type) (cd :: k1) (cc :: GHC.Generics.Meta) (f' :: k2 -> Type). (GHC.Base.Functor f, Generic.Data.Function.Traverse.Constructor.GTraverseC cd cc 0 f f') => Generic.Data.Function.Traverse.NonSum.GTraverseNonSum' cd f (GHC.Generics.C1 cc f')
- Generic.Data.Function.Traverse.Sum: class GTraverseSum' cd f f'
- Generic.Data.Function.Traverse.Sum: instance forall k (f :: Type -> Type) (cd :: GHC.Generics.Meta) (f' :: k -> Type). (GHC.Base.Functor f, Generic.Data.Function.Traverse.Sum.GTraverseSum' cd f f') => Generic.Data.Function.Traverse.Sum.GTraverseSum f (GHC.Generics.D1 cd f')
- Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (cd :: k1) (f :: Type -> Type) (cc :: GHC.Generics.Meta) (f' :: k2 -> Type). (TypeError ...) => Generic.Data.Function.Traverse.Sum.GTraverseSum' cd f (GHC.Generics.C1 cc f')
- Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (cd :: k1) (f :: Type -> Type). (TypeError ...) => Generic.Data.Function.Traverse.Sum.GTraverseSum' cd f GHC.Generics.V1
- Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (f :: Type -> Type) (cd :: k1) (l :: k2 -> Type) (r :: k2 -> Type). (Generic.Data.Function.Traverse.Sum.GenericTraverseSum f, Generic.Data.Function.Traverse.Sum.GTraverseCSum cd f (l GHC.Generics.:+: r), GHC.Generics.Datatype cd) => Generic.Data.Function.Traverse.Sum.GTraverseSum' cd f (l GHC.Generics.:+: r)
+ Generic.Data.Function.Common: AllowSingletonSum :: SumOpts
+ Generic.Data.Function.Common: SumOnly :: SumOpts
+ Generic.Data.Function.Common: data SumOpts
+ Generic.Data.Function.Example: X1 :: X
+ Generic.Data.Function.Example: X2 :: X
+ Generic.Data.Function.Example: Y :: Y
+ Generic.Data.Function.Example: data X
+ Generic.Data.Function.Example: data Y
+ Generic.Data.Function.Example: instance GHC.Base.Monoid Generic.Data.Function.Example.Showly
+ Generic.Data.Function.Example: instance GHC.Base.Semigroup Generic.Data.Function.Example.Showly
+ Generic.Data.Function.Example: instance GHC.Generics.Generic Generic.Data.Function.Example.X
+ Generic.Data.Function.Example: instance GHC.Generics.Generic Generic.Data.Function.Example.Y
+ Generic.Data.Function.Example: instance Generic.Data.Function.FoldMap.Constructor.GenericFoldMap Generic.Data.Function.Example.Showly
+ Generic.Data.Function.Example: showGeneric :: forall {cd} {f} opts asserts a. (Generic a, Rep a ~ D1 cd f, GFoldMapSum opts Showly f, ApplyGCAsserts asserts f) => a -> String
+ Generic.Data.Function.Example: showGeneric' :: forall {cd} {f} asserts a. (Generic a, Rep a ~ D1 cd f, GFoldMapNonSum Showly f, ApplyGCAsserts asserts f) => a -> String
+ Generic.Data.Function.FoldMap.NonSum: instance Generic.Data.Function.FoldMap.NonSum.GFoldMapNonSum m GHC.Generics.V1
+ Generic.Data.Function.FoldMap.NonSum: instance forall k m (l :: k -> Type) (r :: k -> Type). Generic.Data.Function.FoldMap.NonSum.GFoldMapNonSum m (l GHC.Generics.:+: r)
+ Generic.Data.Function.FoldMap.Sum: instance Generic.Data.Function.FoldMap.Sum.GFoldMapSum opts m GHC.Generics.V1
+ Generic.Data.Function.FoldMap.Sum: instance forall k m (c :: GHC.Generics.Meta) (f :: k -> Type). Generic.Data.Function.FoldMap.Sum.GFoldMapCSum m (GHC.Generics.C1 c f) => Generic.Data.Function.FoldMap.Sum.GFoldMapSum 'Generic.Data.Function.Common.AllowSingletonSum m (GHC.Generics.C1 c f)
+ Generic.Data.Function.FoldMap.Sum: instance forall k m (c :: GHC.Generics.Meta) (f :: k -> Type). Generic.Data.Function.FoldMap.Sum.GFoldMapSum 'Generic.Data.Function.Common.SumOnly m (GHC.Generics.C1 c f)
+ Generic.Data.Function.FoldMap.Sum: instance forall k m (l :: k -> Type) (r :: k -> Type) (opts :: Generic.Data.Function.Common.SumOpts). Generic.Data.Function.FoldMap.Sum.GFoldMapCSum m (l GHC.Generics.:+: r) => Generic.Data.Function.FoldMap.Sum.GFoldMapSum opts m (l GHC.Generics.:+: r)
+ Generic.Data.Function.FoldMap.SumConsByte: instance Generic.Data.Function.FoldMap.SumConsByte.GFoldMapSumConsByte m GHC.Generics.V1
+ Generic.Data.Function.FoldMap.SumConsByte: instance forall k m (c :: GHC.Generics.Meta) (f :: k -> Type). Generic.Data.Function.FoldMap.SumConsByte.GFoldMapSumConsByte m (GHC.Generics.C1 c f)
+ Generic.Data.Function.Traverse.NonSum: instance Generic.Data.Function.Traverse.NonSum.GTraverseNonSum cd f GHC.Generics.V1
+ Generic.Data.Function.Traverse.NonSum: instance forall k (cd :: GHC.Generics.Meta) (f :: Type -> Type) (l :: k -> Type) (r :: k -> Type). Generic.Data.Function.Traverse.NonSum.GTraverseNonSum cd f (l GHC.Generics.:+: r)
+ Generic.Data.Function.Traverse.NonSum: instance forall k (f :: Type -> Type) (cd :: GHC.Generics.Meta) (cc :: GHC.Generics.Meta) (f' :: k -> Type). (GHC.Base.Functor f, Generic.Data.Function.Traverse.Constructor.GTraverseC cd cc 0 f f') => Generic.Data.Function.Traverse.NonSum.GTraverseNonSum cd f (GHC.Generics.C1 cc f')
+ Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (cd :: k1) (f :: Type -> Type) (cc :: GHC.Generics.Meta) (f' :: k2 -> Type). Generic.Data.Function.Traverse.Sum.GTraverseSum 'Generic.Data.Function.Common.SumOnly cd f (GHC.Generics.C1 cc f')
+ Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (f :: Type -> Type) (cd :: k1) (cc :: GHC.Generics.Meta) (f' :: k2 -> Type). (Generic.Data.Function.Traverse.Sum.GenericTraverseSum f, Generic.Data.Function.Traverse.Sum.GTraverseCSum cd f (GHC.Generics.C1 cc f'), GHC.Generics.Datatype cd) => Generic.Data.Function.Traverse.Sum.GTraverseSum 'Generic.Data.Function.Common.AllowSingletonSum cd f (GHC.Generics.C1 cc f')
+ Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (f :: Type -> Type) (cd :: k1) (l :: k2 -> Type) (r :: k2 -> Type) (opts :: Generic.Data.Function.Common.SumOpts). (Generic.Data.Function.Traverse.Sum.GenericTraverseSum f, Generic.Data.Function.Traverse.Sum.GTraverseCSum cd f (l GHC.Generics.:+: r), GHC.Generics.Datatype cd) => Generic.Data.Function.Traverse.Sum.GTraverseSum opts cd f (l GHC.Generics.:+: r)
+ Generic.Data.Function.Traverse.Sum: instance forall k1 k2 (opts :: Generic.Data.Function.Common.SumOpts) (cd :: k1) (f :: Type -> Type). Generic.Data.Function.Traverse.Sum.GTraverseSum opts cd f GHC.Generics.V1
+ Generic.Data.Rep.Assert: NeedSum :: GCAssert
+ Generic.Data.Rep.Assert: NoEmpty :: GCAssert
+ Generic.Data.Rep.Assert: NoSum :: GCAssert
+ Generic.Data.Rep.Assert: data GCAssert
+ Generic.Data.Rep.Assert: type family GCNeedSum a
+ Generic.Data.Rep.Error: eNeedSum :: String
+ Generic.Data.Rep.Error: eNoEmpty :: String
+ Generic.Data.Rep.Error: eNoSum :: String
+ Generic.Data.Rep.Error: type ENoEmpty = 'Text "Requested generic instance disallows empty data type"
+ Generic.Data.Rep.Error: type EUnexpectedNonSum = 'Text "Refusing to derive sum generic instance for non-sum data type"
+ Generic.Data.Rep.Error: type EUnexpectedSum = 'Text "Cannot derive non-sum generic instance for sum data type"
+ Generic.Data.Rep.Error: wrapE :: String -> String -> String
- Generic.Data.Function.Example: Showly :: a -> Showly a
+ Generic.Data.Function.Example: Showly :: [String] -> Showly
- Generic.Data.Function.Example: [unShowly] :: Showly a -> a
+ Generic.Data.Function.Example: [unShowly] :: Showly -> [String]
- Generic.Data.Function.Example: newtype Showly a
+ Generic.Data.Function.Example: newtype Showly
- Generic.Data.Function.FoldMap: class GFoldMapSum m f
+ Generic.Data.Function.FoldMap: class GFoldMapSum (opts :: SumOpts) m f
- Generic.Data.Function.FoldMap: genericFoldMapNonSum :: forall m a. (Generic a, GFoldMapNonSum m (Rep a)) => a -> m
+ Generic.Data.Function.FoldMap: genericFoldMapNonSum :: forall {cd} {f} asserts m a. (Generic a, Rep a ~ D1 cd f, GFoldMapNonSum m f, ApplyGCAsserts asserts f) => a -> m
- Generic.Data.Function.FoldMap: genericFoldMapSum :: forall m a. (Generic a, GFoldMapSum m (Rep a)) => (String -> m) -> a -> m
+ Generic.Data.Function.FoldMap: genericFoldMapSum :: forall {cd} {f} opts asserts m a. (Generic a, Rep a ~ D1 cd f, GFoldMapSum opts m f, ApplyGCAsserts asserts f) => (String -> m) -> a -> m
- Generic.Data.Function.FoldMap.Sum: class GFoldMapSum m f
+ Generic.Data.Function.FoldMap.Sum: class GFoldMapSum (opts :: SumOpts) m f
- Generic.Data.Function.FoldMap.Sum: gFoldMapSum :: GFoldMapSum m f => (String -> m) -> f p -> m
+ Generic.Data.Function.FoldMap.Sum: gFoldMapSum :: GFoldMapSum opts m f => (String -> m) -> f p -> m
- Generic.Data.Function.Traverse: class GTraverseNonSum f f'
+ Generic.Data.Function.Traverse: class GTraverseNonSum (cd :: Meta) f f'
- Generic.Data.Function.Traverse: class GTraverseSum f f'
+ Generic.Data.Function.Traverse: class GTraverseSum (opts :: SumOpts) cd f f'
- Generic.Data.Function.Traverse: genericTraverseNonSum :: forall f a. (Generic a, GTraverseNonSum f (Rep a), Functor f) => f a
+ Generic.Data.Function.Traverse: genericTraverseNonSum :: forall {cd} {gf} asserts f a. (Generic a, Rep a ~ D1 cd gf, GTraverseNonSum cd f gf, ApplyGCAsserts asserts f, Functor f) => f a
- Generic.Data.Function.Traverse: genericTraverseSum :: forall f a pt. (Generic a, GTraverseSum f (Rep a), GenericTraverseC f pt, Functor f) => PfxTagCfg pt -> f a
+ Generic.Data.Function.Traverse: genericTraverseSum :: forall {cd} {gf} opts asserts f a pt. (Generic a, Rep a ~ D1 cd gf, GTraverseSum opts cd f gf, ApplyGCAsserts asserts f, GenericTraverseC f pt, Functor f) => PfxTagCfg pt -> f a
- Generic.Data.Function.Traverse.NonSum: class GTraverseNonSum f f'
+ Generic.Data.Function.Traverse.NonSum: class GTraverseNonSum (cd :: Meta) f f'
- Generic.Data.Function.Traverse.NonSum: gTraverseNonSum :: GTraverseNonSum f f' => f (f' p)
+ Generic.Data.Function.Traverse.NonSum: gTraverseNonSum :: GTraverseNonSum cd f f' => f (f' p)
- Generic.Data.Function.Traverse.Sum: class GTraverseSum f f'
+ Generic.Data.Function.Traverse.Sum: class GTraverseSum (opts :: SumOpts) cd f f'
- Generic.Data.Function.Traverse.Sum: gTraverseSum :: (GTraverseSum f f', GenericTraverseC f pt) => PfxTagCfg pt -> f (f' p)
+ Generic.Data.Function.Traverse.Sum: gTraverseSum :: (GTraverseSum opts cd f f', GenericTraverseC f pt) => PfxTagCfg pt -> f (f' p)
- Generic.Data.Function.Traverse.Sum: gTraverseSum' :: (GTraverseSum' cd f f', GenericTraverseC f pt) => PfxTagCfg pt -> f (f' p)
+ Generic.Data.Function.Traverse.Sum: gTraverseSum' :: forall {p} cd f f' pt. (GenericTraverseC f pt, GenericTraverseSum f, GTraverseCSum cd f f', Datatype cd) => PfxTagCfg pt -> f (f' p)
Files
- CHANGELOG.md +7/−0
- README.md +20/−0
- generic-data-functions.cabal +4/−2
- src/Generic/Data/Function/Common.hs +5/−0
- src/Generic/Data/Function/Error.hs +0/−25
- src/Generic/Data/Function/Example.hs +31/−9
- src/Generic/Data/Function/FoldMap.hs +13/−8
- src/Generic/Data/Function/FoldMap/NonSum.hs +6/−10
- src/Generic/Data/Function/FoldMap/Sum.hs +13/−12
- src/Generic/Data/Function/FoldMap/SumConsByte.hs +2/−3
- src/Generic/Data/Function/Traverse.hs +15/−10
- src/Generic/Data/Function/Traverse/NonSum.hs +7/−14
- src/Generic/Data/Function/Traverse/Sum.hs +25/−23
- src/Generic/Data/Rep/Assert.hs +68/−0
- src/Generic/Data/Rep/Error.hs +48/−0
CHANGELOG.md view
@@ -1,3 +1,10 @@+## 0.2.0 (2023-08-04)+* Redesign interface, pushing certain checks out of type classes into top-level+ generic function type signature. It means busier top-level types and more code+ for wrapping them, but it allows for more flexibility and cleans up+ implementation. (And the busyness simply makes explicit the implicit checks+ that were being done before.)+ ## 0.1.1 (2023-07-20) * add work-in-progress store-style generic `foldMap`, encoding constructors by their index, at `Generic.Data.Function.FoldMap.SumConsByte`
README.md view
@@ -73,5 +73,25 @@ classes. Thus, you will likely be dealing in orphans. Instances, that is. That's life, Jim. +### Funky generics+I have made some weird design choices in this library. Here are some rationales.++#### Handling badness on type & term levels+I don't like silently erroring on badly-configured generics usage, e.g. asking+for a function via generics for an empty data type. Originally, I made those+type error, and that was that. But it meant I would write the same instance over+and over again. And that requirement was hidden in a type class implementation.+Really, it'd be nice if I could put such requirements directly in the types of+the functions that have them.++I've done that. Now, on certain *representation errors*, e.g. you tried to use+non-sum generics on a sum type, we runtime error instead. However, there's a+separate layer for making *assertions about generic representation on the type+level*, the use of which is highly suggested.++Note that if you like to write wrappers over generic functions to fill in+certain bits of info, your job just got a lot uglier. Soz. Anything for type+safety my sweet.+ ## License Provided under the MIT license. See `LICENSE` for license text.
generic-data-functions.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: generic-data-functions-version: 0.1.1+version: 0.2.0 synopsis: Familiar functions lifted to generic data types description: Please see README.md. category: Data, Serialization@@ -27,7 +27,7 @@ library exposed-modules: Generic.Data.Function- Generic.Data.Function.Error+ Generic.Data.Function.Common Generic.Data.Function.Example Generic.Data.Function.FoldMap Generic.Data.Function.FoldMap.Constructor@@ -41,6 +41,8 @@ Generic.Data.Function.Util.Generic Generic.Data.Function.Util.TypeNats Generic.Data.Function.Via+ Generic.Data.Rep.Assert+ Generic.Data.Rep.Error other-modules: Paths_generic_data_functions hs-source-dirs:
+ src/Generic/Data/Function/Common.hs view
@@ -0,0 +1,5 @@+module Generic.Data.Function.Common where++data SumOpts+ = SumOnly -- ^ Only "proper" sum types are permitted, no singletons.+ | AllowSingletonSum -- ^ Treat a single constructor as a sum type still.
− src/Generic/Data/Function/Error.hs
@@ -1,25 +0,0 @@--- | Common descriptions for generic data type errors.--module Generic.Data.Function.Error where--import GHC.TypeLits ( ErrorMessage(Text) )---- | Common type error string for when you attempt to use a generic instance--- at an empty data type (e.g. 'Data.Void.Void', 'GHC.Generics.V1').-type ENoEmpty = 'Text "Requested generic instance disallows empty data type"---- | Common type error string for when GHC is asked to derive a non-sum--- instance, but the data type in question turns out to be a sum data type.------ No need to add the data type name here, since GHC's context includes the--- surrounding instance declaration.-type EUnexpectedSum =- 'Text "Cannot derive non-sum generic instance for sum data type"---- | Common type error string for when GHC is asked to derive a sum instance,--- but the data type in question turns out to be a non-sum data type.------ No need to add the data type name here, since GHC's context includes the--- surrounding instance declaration.-type EUnexpectedNonSum =- 'Text "Refusing to derive sum generic instance for non-sum data type"
src/Generic/Data/Function/Example.hs view
@@ -1,14 +1,36 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+ module Generic.Data.Function.Example where -import GHC.Generics ( Generic )-import Generic.Data.Function.FoldMap.Constructor ( GenericFoldMap(..) )+import GHC.Generics+import Generic.Data.Function.FoldMap+import Generic.Data.Rep.Assert -data D a = D1 a | D2 a a deriving stock (Generic, Show)+import Data.List qualified as List -newtype Showly a = Showly { unShowly :: a }- deriving Show via a- deriving (Semigroup, Monoid) via a+--data D a = D1 a | D2 a a deriving stock (Generic, Show)+data X = X1 | X2 deriving stock (Generic)+data Y = Y deriving stock (Generic) -instance GenericFoldMap (Showly String) where- type GenericFoldMapC (Showly String) a = Show a- genericFoldMapF = Showly . show+newtype Showly = Showly { unShowly :: [String] }+ --deriving Show via String+ deriving (Semigroup, Monoid) via [String]++instance GenericFoldMap Showly where+ type GenericFoldMapC Showly a = Show a+ genericFoldMapF = Showly . (\a -> [a]) . show++showGeneric+ :: forall {cd} {f} opts asserts a+ . (Generic a, Rep a ~ D1 cd f, GFoldMapSum opts Showly f, ApplyGCAsserts asserts f)+ => a -> String+showGeneric =+ mconcat . List.intersperse " " . unShowly+ . genericFoldMapSum @opts @asserts (\cstr -> Showly [cstr])++showGeneric'+ :: forall {cd} {f} asserts a+ . (Generic a, Rep a ~ D1 cd f, GFoldMapNonSum Showly f, ApplyGCAsserts asserts f)+ => a -> String+showGeneric' =+ mconcat . List.intersperse " " . unShowly . genericFoldMapNonSum @asserts
src/Generic/Data/Function/FoldMap.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+ {- | 'foldMap' for generic data types. 'foldMap' can be considered a two-step process:@@ -34,6 +36,7 @@ import GHC.Generics +import Generic.Data.Rep.Assert import Generic.Data.Function.FoldMap.NonSum import Generic.Data.Function.FoldMap.Sum import Generic.Data.Function.FoldMap.Constructor@@ -44,25 +47,27 @@ -- -- @a@ must have exactly one constructor. genericFoldMapNonSum- :: forall m a- . (Generic a, GFoldMapNonSum m (Rep a))+ :: forall {cd} {f} asserts m a+ . ( Generic a, Rep a ~ D1 cd f+ , GFoldMapNonSum m f+ , ApplyGCAsserts asserts f) => a -> m-genericFoldMapNonSum = gFoldMapNonSum . from+genericFoldMapNonSum = gFoldMapNonSum . unM1 . from -- | Generic 'foldMap' over a term of sum data type @a@. ----- @a@ must have at least two constructors.--- -- You must provide a function for mapping constructor names to monoidal values. -- -- This is the most generic option, but depending on your string manipulation -- may be slower. genericFoldMapSum- :: forall m a- . (Generic a, GFoldMapSum m (Rep a))+ :: forall {cd} {f} opts asserts m a+ . ( Generic a, Rep a ~ D1 cd f+ , GFoldMapSum opts m f+ , ApplyGCAsserts asserts f) => (String -> m) -> a -> m-genericFoldMapSum f = gFoldMapSum f . from+genericFoldMapSum f = gFoldMapSum @opts f . unM1 . from -- | Generic 'foldMap' over a term of sum data type @a@ where constructors are -- mapped to their index (distance from first/leftmost constructor)
src/Generic/Data/Function/FoldMap/NonSum.hs view
@@ -3,9 +3,8 @@ module Generic.Data.Function.FoldMap.NonSum where import GHC.Generics-import GHC.TypeLits ( TypeError )-import Generic.Data.Function.Error ( type ENoEmpty, type EUnexpectedSum ) import Generic.Data.Function.FoldMap.Constructor ( GFoldMapC(gFoldMapC) )+import Generic.Data.Rep.Error {- | 'foldMap' over generic product data types. @@ -14,14 +13,11 @@ -} class GFoldMapNonSum m f where gFoldMapNonSum :: f p -> m -instance GFoldMapNonSum m f => GFoldMapNonSum m (D1 c f) where- gFoldMapNonSum (M1 a) = gFoldMapNonSum a--instance TypeError EUnexpectedSum => GFoldMapNonSum m (l :+: r) where- gFoldMapNonSum = undefined- instance GFoldMapC m f => GFoldMapNonSum m (C1 c f) where gFoldMapNonSum (M1 a) = gFoldMapC a -instance TypeError ENoEmpty => GFoldMapNonSum m V1 where- gFoldMapNonSum = undefined+instance GFoldMapNonSum m (l :+: r) where+ gFoldMapNonSum = error eNoSum++instance GFoldMapNonSum m V1 where+ gFoldMapNonSum = error eNoEmpty
src/Generic/Data/Function/FoldMap/Sum.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE UndecidableInstances #-} -- required below GHC 9.6 {- | 'foldMap' for sum types where constructors are encoded by mapping the@@ -11,25 +12,25 @@ module Generic.Data.Function.FoldMap.Sum where import GHC.Generics-import GHC.TypeLits ( TypeError ) import Generic.Data.Function.Util.Generic ( conName' )-import Generic.Data.Function.Error ( type ENoEmpty, type EUnexpectedNonSum ) import Generic.Data.Function.FoldMap.Constructor ( GFoldMapC(gFoldMapC) )--class GFoldMapSum m f where- gFoldMapSum :: (String -> m) -> f p -> m+import Generic.Data.Rep.Error+import Generic.Data.Function.Common -instance GFoldMapSum m f => GFoldMapSum m (D1 c f) where- gFoldMapSum f (M1 a) = gFoldMapSum f a+class GFoldMapSum (opts :: SumOpts) m f where gFoldMapSum :: (String -> m) -> f p -> m -instance GFoldMapCSum m (l :+: r) => GFoldMapSum m (l :+: r) where+instance GFoldMapCSum m (l :+: r) => GFoldMapSum opts m (l :+: r) where gFoldMapSum = gFoldMapCSum -instance TypeError EUnexpectedNonSum => GFoldMapSum m (C1 c f) where- gFoldMapSum = undefined+instance GFoldMapSum 'SumOnly m (C1 c f) where+ gFoldMapSum = error eNeedSum -instance TypeError ENoEmpty => GFoldMapSum m V1 where- gFoldMapSum = undefined+instance GFoldMapCSum m (C1 c f)+ => GFoldMapSum 'AllowSingletonSum m (C1 c f) where+ gFoldMapSum = gFoldMapCSum++instance GFoldMapSum opts m V1 where+ gFoldMapSum = error eNoEmpty -- | Sum type handler prefixing constructor contents with their mapped -- constructor name via a provided @String -> m@.
src/Generic/Data/Function/FoldMap/SumConsByte.hs view
@@ -15,7 +15,6 @@ import GHC.TypeLits import Data.Kind ( Type, Constraint ) import Generic.Data.Function.Util.TypeNats ( natVal'' )-import Generic.Data.Function.Error ( type ENoEmpty, type EUnexpectedNonSum ) import Generic.Data.Function.FoldMap.Constructor ( GFoldMapC(gFoldMapC) ) import Data.Word ( Word8 )@@ -35,10 +34,10 @@ gFoldMapSumConsByte f lr = gFoldMapCSumCtrArityByte @m @0 f lr <> gFoldMapCSumCtr lr -instance TypeError EUnexpectedNonSum => GFoldMapSumConsByte m (C1 c f) where+instance GFoldMapSumConsByte m (C1 c f) where gFoldMapSumConsByte _ = undefined -instance TypeError ENoEmpty => GFoldMapSumConsByte m V1 where+instance GFoldMapSumConsByte m V1 where gFoldMapSumConsByte _ = undefined ---
src/Generic/Data/Function/Traverse.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+ {- | 'traverse' for generic data types. TODO This is harder to conceptualize than generic 'foldMap'. No nice clean@@ -16,6 +18,7 @@ import GHC.Generics +import Generic.Data.Rep.Assert import Generic.Data.Function.Traverse.NonSum import Generic.Data.Function.Traverse.Sum import Generic.Data.Function.Traverse.Constructor@@ -23,25 +26,27 @@ import Data.Text qualified as Text -- | Generic 'traverse' over a term of non-sum data type @f a@.------ @f a@ must have exactly one constructor. genericTraverseNonSum- :: forall f a- . (Generic a, GTraverseNonSum f (Rep a), Functor f)+ :: forall {cd} {gf} asserts f a+ . ( Generic a, Rep a ~ D1 cd gf+ , GTraverseNonSum cd f gf+ , ApplyGCAsserts asserts f+ , Functor f) => f a-genericTraverseNonSum = to <$> gTraverseNonSum+genericTraverseNonSum = (to . M1) <$> gTraverseNonSum @cd -- | Generic 'traverse' over a term of sum data type @f a@. ----- @f a@ must have at least two constructors.--- -- You must provide a configuration for how to handle constructors. genericTraverseSum- :: forall f a pt- . (Generic a, GTraverseSum f (Rep a), GenericTraverseC f pt, Functor f)+ :: forall {cd} {gf} opts asserts f a pt+ . ( Generic a, Rep a ~ D1 cd gf+ , GTraverseSum opts cd f gf+ , ApplyGCAsserts asserts f+ , GenericTraverseC f pt, Functor f) => PfxTagCfg pt -> f a-genericTraverseSum ptc = to <$> gTraverseSum ptc+genericTraverseSum ptc = (to . M1) <$> gTraverseSum @opts @cd ptc -- | Construct a prefix tag config using existing 'Eq' and 'Show' instances. --
src/Generic/Data/Function/Traverse/NonSum.hs view
@@ -4,23 +4,16 @@ module Generic.Data.Function.Traverse.NonSum where import GHC.Generics-import GHC.TypeLits ( TypeError )-import Generic.Data.Function.Error ( type ENoEmpty, type EUnexpectedSum ) import Generic.Data.Function.Traverse.Constructor ( GTraverseC(gTraverseC) ) -class GTraverseNonSum f f' where gTraverseNonSum :: f (f' p)--instance (Functor f, GTraverseNonSum' cd f f') => GTraverseNonSum f (D1 cd f') where- gTraverseNonSum = M1 <$> gTraverseNonSum' @cd--class GTraverseNonSum' cd f f' where gTraverseNonSum' :: f (f' p)+class GTraverseNonSum (cd :: Meta) f f' where gTraverseNonSum :: f (f' p) -instance TypeError EUnexpectedSum => GTraverseNonSum' cd f (l :+: r) where- gTraverseNonSum' = undefined+instance GTraverseNonSum cd f (l :+: r) where+ gTraverseNonSum = undefined instance (Functor f, GTraverseC cd cc 0 f f')- => GTraverseNonSum' cd f (C1 cc f') where- gTraverseNonSum' = M1 <$> gTraverseC @cd @cc @0+ => GTraverseNonSum cd f (C1 cc f') where+ gTraverseNonSum = M1 <$> gTraverseC @cd @cc @0 -instance TypeError ENoEmpty => GTraverseNonSum' cd f V1 where- gTraverseNonSum' = undefined+instance GTraverseNonSum cd f V1 where+ gTraverseNonSum = undefined
src/Generic/Data/Function/Traverse/Sum.hs view
@@ -4,10 +4,10 @@ module Generic.Data.Function.Traverse.Sum where import GHC.Generics-import GHC.TypeLits ( TypeError ) import Generic.Data.Function.Util.Generic ( datatypeName', conName' )-import Generic.Data.Function.Error ( type ENoEmpty, type EUnexpectedNonSum ) import Generic.Data.Function.Traverse.Constructor ( GTraverseC(gTraverseC), GenericTraverse(..) )+import Generic.Data.Rep.Error+import Generic.Data.Function.Common import Data.Text ( Text ) import Control.Applicative qualified as Applicative@@ -50,33 +50,35 @@ -- ^ Make a prefix tag human-readable. 'show' is often appropriate. } -class GTraverseSum f f' where+class GTraverseSum (opts :: SumOpts) cd f f' where gTraverseSum :: GenericTraverseC f pt => PfxTagCfg pt -> f (f' p) -instance (Functor f, GTraverseSum' cd f f') => GTraverseSum f (D1 cd f') where- gTraverseSum pt = M1 <$> gTraverseSum' @cd pt+instance (GenericTraverseSum f, GTraverseCSum cd f (l :+: r), Datatype cd)+ => GTraverseSum opts cd f (l :+: r) where+ gTraverseSum = gTraverseSum' @cd -class GTraverseSum' cd f f' where- gTraverseSum' :: GenericTraverseC f pt => PfxTagCfg pt -> f (f' p)+gTraverseSum'+ :: forall {p} cd f f' pt+ . (GenericTraverseC f pt, GenericTraverseSum f, GTraverseCSum cd f f', Datatype cd)+ => PfxTagCfg pt -> f (f' p)+gTraverseSum' ptc = do+ pt <- genericTraverseSumPfxTagAction cd+ gTraverseCSum @cd ptc pt <|> parseErrorNoMatch pt+ where+ cd = datatypeName' @cd+ parseErrorNoMatch pt =+ genericTraverseSumNoMatchingCstrAction cd testedCstrs ((pfxTagCfgShow ptc) pt)+ testedCstrs = [] -- TODO -instance (GenericTraverseSum f, GTraverseCSum cd f (l :+: r), Datatype cd)- => GTraverseSum' cd f (l :+: r) where- gTraverseSum' ptc = do- pt <- genericTraverseSumPfxTagAction cd- gTraverseCSum @cd ptc pt <|> parseErrorNoMatch pt- where- cd = datatypeName' @cd- parseErrorNoMatch pt =- genericTraverseSumNoMatchingCstrAction cd testedCstrs ((pfxTagCfgShow ptc) pt)- testedCstrs = [] -- TODO+instance GTraverseSum 'SumOnly cd f (C1 cc f') where+ gTraverseSum = error eNeedSum --- | Refuse to derive a non-sum instance if we expected a sum data type.-instance TypeError EUnexpectedNonSum => GTraverseSum' cd f (C1 cc f') where- gTraverseSum' = undefined+instance (GenericTraverseSum f, GTraverseCSum cd f (C1 cc f'), Datatype cd)+ => GTraverseSum 'AllowSingletonSum cd f (C1 cc f') where+ gTraverseSum = gTraverseSum' @cd --- | Refuse to derive an instance for an empty data type.-instance TypeError ENoEmpty => GTraverseSum' cd f V1 where- gTraverseSum' = undefined+instance GTraverseSum opts cd f V1 where+ gTraverseSum = error eNoEmpty -- | Generic getter (constructor sum level). class GTraverseCSum cd f f' where
+ src/Generic/Data/Rep/Assert.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE UndecidableInstances #-} -- required below GHC 9.6 TODO maybe untrue for this++{- | Assertions on precise generic data representation.++I like being real picky with my generic code, disallowing misuse at the type+level. However, this makes it less flexible overall, and is a large chunk of the+code I have to write over and over again.++I mainly care about sanity checks, along the lines of "if the generic+representations looks like this, type error out". So they don't make any+term-level changes.++So, instead of hiding these in generic type class instances, I put them in type+family equations. Now we can turn these checks on and off -- and when they're+on, you have to carry around that fact in your types. Fantastic!++Checks are formed as 'Constraint's, where a failure triggers a 'TypeError' and a+success goes to the empty constraint '()' (@'()' :: 'Constraint'@ as well as+'Type').++These checks were always done on the type-level, but they were "inline" with the+rest of the type class. By pulling them out, we *should* be incurring some+compile-time performance penalty (albeit hopefully minor due to the simple+nature of the checks), but making no change to runtime.+-}++module Generic.Data.Rep.Assert where++import GHC.Generics+import GHC.TypeLits ( TypeError )+import Generic.Data.Rep.Error++import Data.Kind++-- | Generic representation assertions, on the constructor level (bits that come+-- after 'D1').+data GCAssert+ = NoEmpty -- ^ Is not an empty type (does not have 0 constructors)+ | NoSum -- ^ Is not a sum type (has 0 or 1 constructors)+ | NeedSum -- ^ Is a sum type (has 0 or >2 constructors)++-- | Convert a generic representation constructor-level assertion "label" to the+-- assertion it represents, and make that assertion.+type family ApplyGCAssert x a where+ ApplyGCAssert 'NoEmpty a = GCNoEmpty a+ ApplyGCAssert 'NoSum a = GCNoSum a+ ApplyGCAssert 'NeedSum a = GCNeedSum a++-- | Apply a list of generic representation constructor-level assertions.+type ApplyGCAsserts :: [GCAssert] -> (k -> Type) -> Constraint+type family ApplyGCAsserts ls a where+ ApplyGCAsserts '[] a = ()+ ApplyGCAsserts (l ': ls) a = (ApplyGCAssert l a, ApplyGCAsserts ls a)++type GCNoEmpty :: (k -> Type) -> Constraint+type family GCNoEmpty a where+ GCNoEmpty V1 = TypeError ENoEmpty+ GCNoEmpty a = ()++type GCNoSum :: (k -> Type) -> Constraint+type family GCNoSum a where+ GCNoSum (_ :+: _) = TypeError EUnexpectedSum+ GCNoSum a = ()++type GCNeedSum :: (k -> Type) -> Constraint+type family GCNeedSum a where+ GCNeedSum (C1 _ _) = TypeError EUnexpectedNonSum+ GCNeedSum a = ()
+ src/Generic/Data/Rep/Error.hs view
@@ -0,0 +1,48 @@+{- | Common descriptions for common generic data representation errors. Type+ level (compile time) and term level (runtime).++TODO: if this package ever expands, these deserve plenty of attention, like+generic-optics has.++Runtime errors are a bit meatier because it's easy to do so, and I don't want+people to see them more than once (really you should use the typing support).+-}++module Generic.Data.Rep.Error where++import GHC.TypeLits ( ErrorMessage(Text) )++wrapE :: String -> String -> String+wrapE msgGot msgWhyBad =+ "Generic.Data.Rep.Error:\n"+ <> "Attempted to use an invalid generic instance: \n"+ <> "got: "<>msgGot<>"\n"+ <> "but: "<>msgWhyBad<>"\n"+ <> "You can likely catch such errors during compilation.\n"+ <> "See the generic-data-functions package on Hackage."++-- | Common type error string for when you attempt to use a generic instance+-- at an empty data type (e.g. 'Data.Void.Void', 'GHC.Generics.V1').+type ENoEmpty = 'Text "Requested generic instance disallows empty data type"+eNoEmpty :: String+eNoEmpty = wrapE "empty data type" "disallowed"++-- | Common type error string for when GHC is asked to derive a non-sum+-- instance, but the data type in question turns out to be a sum data type.+--+-- No need to add the data type name here, since GHC's context includes the+-- surrounding instance declaration.+type EUnexpectedSum =+ 'Text "Cannot derive non-sum generic instance for sum data type"+eNoSum :: String+eNoSum = wrapE "sum data type" "cannot use non-sum generics"++-- | Common type error string for when GHC is asked to derive a sum instance,+-- but the data type in question turns out to be a non-sum data type.+--+-- No need to add the data type name here, since GHC's context includes the+-- surrounding instance declaration.+type EUnexpectedNonSum =+ 'Text "Refusing to derive sum generic instance for non-sum data type"+eNeedSum :: String+eNeedSum = wrapE "non-sum data type" "cannot use sum-only generics"