packages feed

generic-data-asserts (empty) → 0.1.1

raw patch · 6 files changed

+205/−0 lines, 6 filesdep +basedep +generic-data-assertsdep +type-spec

Dependencies added: base, generic-data-asserts, type-spec

Files

+ CHANGELOG.md view
@@ -0,0 +1,4 @@+## 0.1.1 (2024-04-04)+Initial release.++  * extracted from generic-data-functions
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2024 Ben Orchard (@raehik) <thefirstmuffinman@gmail.com>++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,19 @@+[gdf-hackage]: https://hackage.haskell.org/package/generic-data-functions++# generic-data-asserts+Structural assertions on the generic data representations (`GHC.Generic.Rep a`).++Sometimes, we want to write generics that only work on certain data types with a+certain shape e.g. non-sum types (single constructor). Achieving this is fairly+straightforward if we add a `TypeError` constraint on the relevant generic+representation unwrapping instance (here, the `(:+:)` constructor sum type).++This library effectively pulls those checks out of generic code and runs them by+separately. This way, we can simplify our generics, and make them more flexible+(e.g. a user may choose whether to permit void types at compile time or not).++This began as a minor feature in my [generic-data-functions][gdf-hackage]+library.++## License+Provided under the MIT license. See `LICENSE` for license text.
+ generic-data-asserts.cabal view
@@ -0,0 +1,73 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.35.2.+--+-- see: https://github.com/sol/hpack++name:           generic-data-asserts+version:        0.1.1+synopsis:       Structural assertions on generic data representations.+description:    Please see README.md.+category:       Data, Serialization+homepage:       https://github.com/raehik/generic-rep-asserts#readme+bug-reports:    https://github.com/raehik/generic-rep-asserts/issues+author:         Ben Orchard+maintainer:     Ben Orchard <thefirstmuffinman@gmail.com>+license:        MIT+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/raehik/generic-rep-asserts++library+  exposed-modules:+      Generic.Data.Rep.Assert+  other-modules:+      Paths_generic_data_asserts+  hs-source-dirs:+      src+  default-extensions:+      LambdaCase+      NoStarIsType+      DerivingVia+      DeriveAnyClass+      GADTs+      RoleAnnotations+      DefaultSignatures+      TypeFamilies+      DataKinds+      MagicHash+  ghc-options: -Wall+  build-depends:+      base >=4.14 && <5+  default-language: GHC2021++test-suite spec+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules:+      Paths_generic_data_asserts+  hs-source-dirs:+      test+  default-extensions:+      LambdaCase+      NoStarIsType+      DerivingVia+      DeriveAnyClass+      GADTs+      RoleAnnotations+      DefaultSignatures+      TypeFamilies+      DataKinds+      MagicHash+  ghc-options: -Wall+  build-depends:+      base >=4.14 && <5+    , generic-data-asserts+    , type-spec >=0.4.0.0 && <0.5+  default-language: GHC2021
+ src/Generic/Data/Rep/Assert.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE UndecidableInstances #-} -- for IsEnum++-- | Structural assertions on generic data representation.++module Generic.Data.Rep.Assert where++import GHC.Generics+import GHC.TypeError+import GHC.TypeLits ( type Symbol )+import Data.Kind ( type Constraint )++type GAssertError :: k -> Symbol -> Constraint+type GAssertError a msg = TypeError+         ('Text "Assertion on generic representation failed for type: "+    :<>: 'ShowType a+    :$$: 'Text "Message: " :<>: 'Text msg)++type family StripD1 a where StripD1 (D1 _ a) = a++-- | Type is not void i.e. has at least one constructor.+type GAssertNotVoid a = Assert (IsNotVoid (StripD1 (Rep a)))+    (GAssertError a "not non-void type (>=1 constructor)")+type family IsNotVoid a where+    IsNotVoid V1  = False+    IsNotVoid _ = True++-- | Type is not a sum type i.e. has at most one constructor.+--+-- Permits void types.+type GAssertNotSum a = Assert (IsNotSum (StripD1 (Rep a)))+    (GAssertError a "not non-sum type (1 constructor)")+type family IsNotSum a where+    IsNotSum (_ :+: _) = False+    IsNotSum _ = True++-- | Type is a sum type i.e. has >=2 constructors.+--+-- Permits void types.+type GAssertSum a = Assert (IsSum (StripD1 (Rep a)))+    (GAssertError a "not sum type (>=2 constructors)")+type family IsSum a where+    IsSum (C1 _ _) = False+    IsSum _ = True++-- | Type has only empty constructors.+--+-- Permits void types.+type GAssertEnum a = Assert (IsEnum (StripD1 (Rep a)))+    (GAssertError a "not enum type (all empty constructors)")+type family IsEnum a where+    IsEnum V1 = True+    IsEnum (C1 _ a) = ConsIsEmpty a+    IsEnum (l :+: r) = IsEnum l `And` IsEnum r++type family ConsIsEmpty a where+    ConsIsEmpty U1 = True+    ConsIsEmpty _  = False++-- | Type level boolean AND.+type family And l r where+    And True True = True+    And _    _    = False
+ test/Main.hs view
@@ -0,0 +1,27 @@+module Main where++import Test.TypeSpec+import GHC.Generics+import Generic.Data.Rep.Assert+import Data.Kind++data DSum = DSum1 () | DSum2 deriving stock Generic+data DEnum = DEnum1 | DEnum2 | DEnum3 deriving stock Generic+data DNonSum = DNonSum () () deriving stock Generic+data DVoid deriving stock Generic++main :: IO ()+main = print spec++spec+    :: Expect '[ AssertPasses (GAssertNotVoid DSum)+               , AssertPasses (GAssertSum DSum)+               , AssertPasses (GAssertNotSum DNonSum)+               , AssertPasses (GAssertEnum DEnum)+               ]+spec = Valid++type AssertPasses a = Is a (() :: Constraint)++-- can't write this because GHC finds the TypeError before type-spec+--type AssertFails a = Isn't a (() :: Constraint)