packages feed

strongweak 0.7.1 → 0.8.0

raw patch · 9 files changed

+92/−104 lines, 9 filesdep −eitherdep ~text

Dependencies removed: either

Dependency ranges changed: text

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+## 0.8.0 (2024-06-11)+* no longer use `Validation`: failures now must wrap explicitly instead of being+  implicitly collated+* fix some bounds (`text` lower bound was too low)+* various tweaks+ ## 0.7.1 (2024-05-27) * bump meta/dependencies 
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2022 Ben Orchard (@raehik) <thefirstmuffinman@gmail.com>+Copyright (c) 2022-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
src/Strongweak/Strengthen.hs view
@@ -6,13 +6,13 @@   -- * 'Strengthen' class     Strengthen(..)   , restrengthen-  , Result    -- ** Helpers   , strengthenBounded    -- * Strengthen failures   , StrengthenFailure(..)+  , StrengthenFailure'   , failStrengthen1   , failStrengthen @@ -20,11 +20,8 @@   , Strongweak.Weaken.Weak   ) where -import Strongweak.Util.Typeable ( typeRep' ) import Strongweak.Util.TypeNats ( natVal'' ) import Strongweak.Weaken ( Weaken(..) )-import Data.Either.Validation-import Data.Typeable ( Typeable )  import GHC.TypeNats ( KnownNat ) import Data.Word@@ -42,7 +39,7 @@  import Data.Bits ( FiniteBits ) -type Result = Validation StrengthenFailure+import Data.Typeable ( Typeable, TypeRep, typeRep, Proxy(Proxy) )  {- | Attempt to strengthen some @'Weak' a@, asserting certain invariants. @@ -57,7 +54,7 @@ class Weaken a => Strengthen a where     -- | Attempt to strengthen some @'Weak' a@ to its associated strong type     --   @a@.-    strengthen :: Weak a -> Result a+    strengthen :: Weak a -> Either StrengthenFailure' a  -- | Weaken a strong value, then strengthen it again. --@@ -66,35 +63,48 @@ -- invariants. For example: -- -- >>> restrengthen $ unsafeStrengthen @(Vector 2 Natural) [0]--- Failure ...-restrengthen-    :: (Strengthen a, Weaken a)-    => a -> Result a+-- Left ...+restrengthen :: (Strengthen a, Weaken a) => a -> Either StrengthenFailure' a restrengthen = strengthen . weaken  -- | A failure encountered during strengthening.-data StrengthenFailure = StrengthenFailure-  { strengthenFailDetail :: [TBL.Builder]+--+-- Strengthening can involve multiple distinct checks. In such cases, you may+-- record multiple failures in a single 'StrengthenFailure' by placing them in+-- the inner failure list and noting their meaning in the detail field.+data StrengthenFailure text = StrengthenFailure+  { strengthenFailDetail :: [text]   -- ^ Detail on strengthen failure.   --   -- We use a list here for the cases where you want multiple lines of detail.   -- Separating with a newline would make prettifying later harder, so we delay.+  --+  -- Note that this should probably never be empty. TODO consider @NonEmpty@,+  -- but fairly unimportant. -  , strengthenFailInner  :: [(TBL.Builder, StrengthenFailure)]-  -- ^ Indexed.+  , strengthenFailInner  :: [(text, StrengthenFailure text)]+  -- ^ Optional wrapped failures.+  --+  -- The @text@ type acts as an index. Its meaning depends on the failure+  -- in question, and should be explained in 'strengthenFailDetail'.   } deriving stock Show +type StrengthenFailure' = StrengthenFailure TBL.Builder++-- | Shorthand for failing a strengthen. failStrengthen-    :: [TBL.Builder] -> [(TBL.Builder, StrengthenFailure)] -> Result a-failStrengthen t fs = Failure $ StrengthenFailure t fs+    :: [text] -> [(text, StrengthenFailure text)]+    -> Either (StrengthenFailure text) a+failStrengthen t fs = Left $ StrengthenFailure t fs -failStrengthen1 :: [TBL.Builder] -> Result a+-- | Shorthand for failing a strengthen with no inner failures.+failStrengthen1 :: [text] -> Either (StrengthenFailure text) a failStrengthen1 t = failStrengthen t []  -- | Strengthen a type by refining it with a predicate. instance Refine p a => Strengthen (Refined p a) where     strengthen = refine .> \case-      Right ra -> Success ra+      Right ra -> Right ra       Left  rf -> failStrengthen1         [ "refinement failure:"         , TBL.fromText (prettyRefineFailure rf) ]@@ -103,7 +113,7 @@ -- | Strengthen a type by refining it with a functor predicate. instance Refine1 p f => Strengthen (Refined1 p f a) where     strengthen = refine1 .> \case-      Right ra -> Success ra+      Right ra -> Right ra       Left  rf -> failStrengthen1         [ "refinement failure:"         , TBL.fromText (prettyRefineFailure rf) ]@@ -111,7 +121,7 @@ -- | Strengthen a plain list into a non-empty list by asserting non-emptiness. instance Strengthen (NonEmpty a) where     strengthen = NonEmpty.nonEmpty .> \case-      Just neas -> Success neas+      Just neas -> Right neas       Nothing   -> failStrengthen1 $         [ "type: [a] -> NonEmpty a"         , "fail: empty list" ]@@ -121,7 +131,7 @@     -- TODO another case of TBL not supporting unbounded integrals     strengthen as =         case VGS.fromList as of-          Just va -> Success va+          Just va -> Right va           Nothing -> failStrengthen1 $             [ "type: [a] -> Vector v "<>fromString (show n)<>" a"             , "fail: wrong length (got "<>TBL.fromDec (length as)<>")" ]@@ -129,11 +139,11 @@  -- | Add wrapper. instance Strengthen (Identity a) where-    strengthen = Success . Identity+    strengthen = Right . Identity  -- | Add wrapper. instance Strengthen (Const a b) where-    strengthen = Success . Const+    strengthen = Right . Const  {- TODO controversial. seems logical, but also kinda annoying. instance (Show a, Typeable a) => Strengthen (Maybe a) where@@ -165,9 +175,9 @@     :: forall m n     .  ( Typeable n, Integral n, Show n        , Typeable m, Integral m, Bounded m, FiniteBits m-       ) => n -> Result m+       ) => n -> Either StrengthenFailure' m strengthenBounded n-  | n <= maxBn && n >= minBn = Success (fromIntegral n)+  | n <= maxBn && n >= minBn = Right (fromIntegral n)   | otherwise = failStrengthen1         [ "numeric strengthen: "<>fromString (show (typeRep' @n))           <>" -> "<>fromString (show (typeRep' @m))@@ -187,36 +197,36 @@ instance Strengthen a => Strengthen [a] where     strengthen = strengthenList -strengthenList :: Strengthen a => [Weak a] -> Result [a]+strengthenList :: Strengthen a => [Weak a] -> Either StrengthenFailure' [a] strengthenList = goR (0 :: Int) [] . map strengthen   where     goR i as = \case       r:rs ->         case r of-          Success a -> goR (i+1) (a:as) rs-          Failure e -> goL (i+1) [(TBL.fromDec i, e)]    rs-      []   -> Success as+          Right a -> goR (i+1) (a:as) rs+          Left  e -> goL (i+1) [(TBL.fromDec i, e)]    rs+      []   -> Right as     goL i es = \case       r:rs ->         case r of-          Success _ -> goL (i+1) es                      rs-          Failure e -> goL (i+1) ((TBL.fromDec i, e):es) rs+          Right _ -> goL (i+1) es                      rs+          Left  e -> goL (i+1) ((TBL.fromDec i, e):es) rs       []   -> failStrengthen ["list had failures"] es  -- | Decomposer. Strengthen both elements of a tuple. instance (Strengthen l, Strengthen r) => Strengthen (l, r) where     strengthen (l, r) =         case strengthen l of-          Success sl ->+          Right sl ->             case strengthen r of-              Success sr -> Success (sl, sr)-              Failure er -> failStrengthen ["2-tuple: right failed"]+              Right sr -> Right (sl, sr)+              Left  er -> failStrengthen ["2-tuple: right failed"]                 [("R", er)]-          Failure el ->+          Left  el ->             case strengthen @r r of-              Success _  -> failStrengthen ["2-tuple:  left failed"]+              Right _  -> failStrengthen ["2-tuple:  left failed"]                 [("L", el)]-              Failure er -> failStrengthen ["2-tuple:   l&r failed"]+              Left  er -> failStrengthen ["2-tuple:   l&r failed"]                 [("R", er), ("L", el)]  -- | Decomposer. Strengthen either side of an 'Either'.@@ -229,3 +239,6 @@ -- from flow (.>) :: (a -> b) -> (b -> c) -> a -> c f .> g = g . f++typeRep' :: forall a. Typeable a => TypeRep+typeRep' = typeRep (Proxy @a)
src/Strongweak/Strengthen/Generic.hs view
@@ -17,46 +17,28 @@  module Strongweak.Strengthen.Generic where +import Data.Kind ( type Constraint ) import Strongweak.Strengthen import GHC.Generics-import Data.Either.Validation import Data.Kind ( Type ) import GHC.TypeNats ( Natural, type (+), KnownNat )-import Control.Applicative qualified as A -- liftA2 export workaround import Strongweak.Util.TypeNats ( natVal'' ) import Data.Text.Builder.Linear qualified as TBL import GHC.Exts ( Symbol, fromString, proxy# ) import GHC.TypeLits ( KnownSymbol, symbolVal' ) -{- TODO-So, now that we're improving the error story, we can do so here as well.--At product level in these generics, we know that neither data type names or-constructor names (weak or strong) will change. So individual fields can simply-annotate themselves with the weak & strong field identifiers. Then those can get-wrapped into a nice clean error higher up, that says "this constructor had the-following errors".--It's gonna look like the tuple and list 'Strengthen' instances but worse. Lots-of fiddly stuff.--Also, we can do the data type equality check I noted earlier. If weak & strong-data type names/constructor names match, we're probably doing @SW@ tricks, and-could probably shorten the error a bit.--}- -- | Strengthen a value generically. -- -- The weak and strong types must be /compatible/. See 'Strongweak.Generic' for -- the definition of compatibility in this context. strengthenGeneric     :: (Generic w, Generic s, GStrengthenD (Rep w) (Rep s))-    => w -> Result s+    => w -> Either StrengthenFailure' s strengthenGeneric = fmap to . gstrengthenD . from  -- | Generic strengthening at the datatype level. class GStrengthenD w s where-    gstrengthenD :: w p -> Result (s p)+    gstrengthenD :: w p -> Either StrengthenFailure' (s p)  -- | Strengthen a generic data type, replacing its metadata wrapping. instance GStrengthenC wdn sdn w s@@ -67,10 +49,10 @@  -- | Generic strengthening at the constructor sum level. class GStrengthenC (wdn :: Symbol) (sdn :: Symbol) w s where-    gstrengthenC :: w p -> Result (s p)+    gstrengthenC :: w p -> Either StrengthenFailure' (s p)  -- | Nothing to do for empty datatypes.-instance GStrengthenC wdn sdn V1 V1 where gstrengthenC = Success+instance GStrengthenC wdn sdn V1 V1 where gstrengthenC = Right  -- | Strengthen sum types by casing and strengthening left or right. instance@@ -87,11 +69,11 @@         (C1 (MetaCons scn _smc2 _smc3) s) where     gstrengthenC (M1 w) =         case gstrengthenS @0 w of-          Success s  -> Success (M1 s)-          Failure es -> failStrengthen [reifyCstrs @wcd @wcn @scd @scn] es+          Right s -> Right (M1 s)+          Left  e -> failStrengthen [reifyCstrs @wcd @wcn @scd @scn] e -class ReifyCstrs (ld :: Symbol) (lc :: Symbol) (rd :: Symbol) (rc :: Symbol) where-    reifyCstrs :: TBL.Builder+type ReifyCstrs :: Symbol -> Symbol -> Symbol -> Symbol -> Constraint+class ReifyCstrs ld lc rd rc where reifyCstrs :: TBL.Builder  -- | Special case: data type and constructor names are equivalent: simplify instance {-# OVERLAPPING #-} (KnownSymbol d, KnownSymbol c)@@ -114,10 +96,10 @@  -- | Generic strengthening at the constructor level. class GStrengthenS (i :: Natural) w s where-    gstrengthenS :: w p -> Validation [(TBL.Builder, StrengthenFailure)] (s p)+    gstrengthenS :: w p -> Either [(TBL.Builder, StrengthenFailure')] (s p)  -- | Nothing to do for empty constructors.-instance GStrengthenS i U1 U1 where gstrengthenS = Success+instance GStrengthenS i U1 U1 where gstrengthenS = Right  -- | Strengthen product types by strengthening left and right. instance@@ -125,9 +107,17 @@   , GStrengthenS (i + ProdArity wl) wr sr   ) => GStrengthenS i (wl :*: wr) (sl :*: sr) where     gstrengthenS (l :*: r) =-        A.liftA2 (:*:)-               (gstrengthenS @i                  l)-               (gstrengthenS @(i + ProdArity wl) r)+        -- With @Validation@, @A.liftA2 (:*:)@. But I don't have a use for it+        -- elsewhere, so let's just be explicit.+        case gstrengthenS @i l of+          Right la ->+            case gstrengthenS @(i + ProdArity wl) r of+              Right  ra -> Right (la :*: ra)+              Left   re -> Left re+          Left  le ->+            case gstrengthenS @(i + ProdArity wl) @_ @sr r of+              Right _ra -> Left le+              Left   re -> Left (le <> re)  -- | Special case: if source and target types are equivalent, just replace meta. --@@ -137,7 +127,7 @@ instance {-# OVERLAPPING #-} GStrengthenS i   (S1 (MetaSel _wms1 _wms2 _wms3 _wms4) (Rec0 a))   (S1 (MetaSel _sms1 _sms2 _sms3 _sms4) (Rec0 a)) where-    gstrengthenS = Success . M1 . unM1+    gstrengthenS = Right . M1 . unM1  -- | Strengthen a field using the existing 'Strengthen' instance. instance@@ -148,8 +138,8 @@         (S1 (MetaSel wmr _wms2 _wms3 _wms4) (Rec0 w))         (S1 (MetaSel smr _sms2 _sms3 _sms4) (Rec0 s)) where     gstrengthenS = unM1 .> unK1 .> strengthen .> \case-      Success s -> Success $ M1 $ K1 s-      Failure e -> Failure [(reifySelector @i @wmr @smr, e)]+      Right s -> Right $ M1 $ K1 s+      Left  e -> Left  [(reifySelector @i @wmr @smr, e)]  {- TODO * how to separate index and record name? @.@ is good and bad, uses same syntax
− src/Strongweak/Util/Text.hs
@@ -1,7 +0,0 @@-module Strongweak.Util.Text where--import Data.Text qualified as Text-import Data.Text ( Text )--tshow :: Show a => a -> Text-tshow = Text.pack . show
− src/Strongweak/Util/Typeable.hs
@@ -1,8 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}--module Strongweak.Util.Typeable where--import Data.Typeable--typeRep' :: forall a. Typeable a => TypeRep-typeRep' = typeRep (Proxy @a)
strongweak.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           strongweak-version:        0.7.1+version:        0.8.0 synopsis:       Convert between strong and weak representations of types description:    Please see README.md. category:       Data@@ -34,8 +34,6 @@       Strongweak.Strengthen       Strongweak.Strengthen.Generic       Strongweak.Strengthen.Unsafe-      Strongweak.Util.Text-      Strongweak.Util.Typeable       Strongweak.Util.TypeNats       Strongweak.Weaken       Strongweak.Weaken.Generic@@ -57,12 +55,11 @@   ghc-options: -Wall -Wno-unticked-promoted-constructors   build-depends:       base >=4.18 && <5-    , either >=5.0.1.1 && <5.1     , rerefined >=0.5.0 && <0.6-    , text >=1.2.5.0 && <2.2+    , text >=2.0 && <2.2     , text-builder-linear >=0.1.2 && <0.2     , vector >=0.12.3.1 && <0.14-    , vector-sized >=1.5.0 && <1.6+    , vector-sized >=1.5.0 && <1.7   default-language: GHC2021  test-suite spec@@ -91,14 +88,13 @@   build-depends:       QuickCheck >=2.14.2 && <2.16     , base >=4.18 && <5-    , either >=5.0.1.1 && <5.1     , generic-random >=1.5.0.1 && <1.6     , hspec >=2.7 && <2.12     , quickcheck-instances >=0.3.26 && <0.4     , rerefined >=0.5.0 && <0.6     , strongweak-    , text >=1.2.5.0 && <2.2+    , text >=2.0 && <2.2     , text-builder-linear >=0.1.2 && <0.2     , vector >=0.12.3.1 && <0.14-    , vector-sized >=1.5.0 && <1.6+    , vector-sized >=1.5.0 && <1.7   default-language: GHC2021
test/Common.hs view
@@ -1,7 +1,7 @@ module Common where  import Strongweak-import Strongweak.Strengthen qualified as Strengthen+import Strongweak.Strengthen import Strongweak.Generic import Rerefined import Rerefined.Predicates@@ -11,7 +11,6 @@ import Numeric.Natural ( Natural ) import Data.Word import Test.QuickCheck.Instances.Natural()-import Data.Either.Validation  data DS (s :: Strength)   = DS0 (SW s Word8) (SW s Word8) Word8 (SW s Word8) (SW s Word8)@@ -52,5 +51,5 @@     weaken = weakenGeneric instance Strengthen (DP 'Strong) where strengthen = strengthenGeneric -tryStrengthenSuccessEq :: Eq a => a -> Strengthen.Result a -> Bool-tryStrengthenSuccessEq a = \case Success a' -> a == a'; Failure{} -> False+tryStrengthenSuccessEq :: Eq a => a -> Either StrengthenFailure' a -> Bool+tryStrengthenSuccessEq a = \case Right a' -> a == a'; Left{} -> False
test/Strongweak/LawsSpec.hs view
@@ -2,7 +2,6 @@  import Strongweak import Common-import Data.Either.Validation import Test.Hspec import Test.Hspec.QuickCheck @@ -14,6 +13,6 @@     prop "strengthen-weaken-strengthen roundtrip partial isomorphism (generic)" $ do       \(dw :: DS 'Weak) ->         case strengthen @(DS 'Strong) dw of-          Failure{}  -> pure ()-          Success ds ->+          Right ds ->             strengthen (weaken ds) `shouldSatisfy` tryStrengthenSuccessEq ds+          Left{}  -> pure ()