strongweak 0.4.0 → 0.4.1
raw patch · 8 files changed
+81/−34 lines, 8 files
Files
- CHANGELOG.md +3/−0
- src/Strongweak.hs +9/−0
- src/Strongweak/Generic.hs +4/−0
- src/Strongweak/Generic/Via.hs +47/−0
- src/Strongweak/Strengthen.hs +6/−10
- src/Strongweak/Strengthen/Unsafe.hs +6/−8
- src/Strongweak/Weaken.hs +4/−15
- strongweak.cabal +2/−1
CHANGELOG.md view
@@ -1,3 +1,6 @@+## 0.4.1 (2023-02-22)+ * add `DerivingVia` wrapper for generic instances (like `Generically`)+ ## 0.4.0 (2023-02-22) * redesign some instances to avoid the decomposer style * alter `Identity`, `Const` instances
src/Strongweak.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE UndecidableInstances #-} module Strongweak ( -- * Instance design@@ -12,6 +13,14 @@ import Strongweak.Strengthen {- $strongweak-instance-design++A given strong type @a@ has exactly one associated weak type @'Weak' a@.+Multiple strong types may weaken to the same weak type.++The following laws must hold:++ * @'weaken' a == 'weaken' b |= a == b@+ * @'strengthen' ('weaken' a) == 'pure' a@ strongweak is largely a programmer convenience library. There is a lot of room to write instances which may seem useful on first glance, but are inconsistent
src/Strongweak/Generic.hs view
@@ -8,10 +8,14 @@ -- * Generic derivers weakenGeneric , strengthenGeneric++ -- * Generic wrapper+ , GenericallySW(..) ) where import Strongweak.Generic.Weaken import Strongweak.Generic.Strengthen+import Strongweak.Generic.Via {- $generic-derivation-compatibility
+ src/Strongweak/Generic/Via.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE UndecidableInstances #-} -- required due to nested constraints++module Strongweak.Generic.Via where++import Strongweak.Generic.Weaken+import Strongweak.Generic.Strengthen+import Strongweak+import GHC.Generics+import Data.Kind++{- | @DerivingVia@ wrapper for strongweak instances.++We can't use 'Generically' conveniently because we need to talk about two data+types, not one -- we would have to do something like @'Generically' ('Tagged' w+s)@, which is ugly. So we instead define our own adorable little "via type"+here!++Use like so:++@+data XYZ (s :: Strength) = XYZ+ { xyz1 :: SW s Word8+ , xyz2 :: Word8+ , xyz3 :: ()+ } deriving stock Generic+deriving via (GenericallySW (XYZ 'Strong) (XYZ 'Weak)) instance Weaken (XYZ 'Strong)+deriving via (GenericallySW (XYZ 'Strong) (XYZ 'Weak)) instance Strengthen (XYZ 'Strong)+@++TODO can't figure out a way around multiple standalone deriving declarations :(+-}++newtype GenericallySW s (w :: Type) = GenericallySW { unGenericallySW :: s }++instance+ ( Generic s, Generic w+ , GWeaken (Rep s) (Rep w)+ ) => Weaken (GenericallySW s w) where+ type Weak (GenericallySW s w) = w+ weaken = weakenGeneric . unGenericallySW++instance+ ( Generic s, Generic w+ , GStrengthenD (Rep w) (Rep s)+ , Weaken (GenericallySW s w)+ ) => Strengthen (GenericallySW s w) where+ strengthen = fmap GenericallySW . strengthenGeneric
src/Strongweak/Strengthen.hs view
@@ -40,23 +40,19 @@ import Data.List.NonEmpty ( NonEmpty( (:|) ) ) import Data.List.NonEmpty qualified as NonEmpty -{- | You may attempt to transform a @'Weak' a@ to an @a@.--Laws:-- * @a === b -> 'strengthen' a === 'strengthen' b@- * @'strengthen' ('weaken' a) === 'Success' a@+{- | Attempt to strengthen some @'Weak' a@, asserting certain invariants. We take 'Weaken' as a superclass in order to maintain strong/weak type pair consistency. We choose this dependency direction because we treat the strong type as the "canonical" one, so 'Weaken' is the more natural (and-straightforward) class to define.+straightforward) class to define. That does mean the instances for this class+are a little confusingly worded. Alas. -Instances should /either/ handle an invariant, or decompose. See "Strongweak"-for a discussion on this design.+See "Strongweak" for class design notes and laws. -} class Weaken a => Strengthen a where- -- | Attempt to transform a weak value to its associated strong one.+ -- | Attempt to strengthen some @'Weak' a@ to its associated strong type+ -- @a@. strengthen :: Weak a -> Validation (NonEmpty StrengthenFail) a -- | Weaken a strong value, then strengthen it again.
src/Strongweak/Strengthen/Unsafe.hs view
@@ -15,6 +15,9 @@ {- | Unsafely transform a @'Weak' a@ to an @a@, without asserting invariants. +Naturally, you must only even /consider/ using this if you have a guarantee that+your value is safe to treat as strong.+ For example, you may unsafely strengthen some @'Numeric.Natural.Natural' n@ into a 'Word8' by unsafely coercing the value, ignoring the possibility that @n >= 255@.@@ -24,17 +27,12 @@ * Numeric coercions should safely overflow. * Some will raise an error (e.g. 'NonEmpty').- * Others will appear to work, but later explode your computer (sized vectors- will probably do this).--Only consider using this if you have a guarantee that your value is safe to-treat as strong.+ * Others will appear to work, but later explode your computer. -Instances should /either/ handle an invariant, or decompose. See "Strongweak"-for a discussion on this design.+See "Strongweak" for class design notes and laws. -} class Weaken a => UnsafeStrengthen a where- -- | Unsafely transform a weak value to its associated strong one.+ -- | Unsafely transform a @'Weak' a@ to its associated strong type @a@. unsafeStrengthen :: Weak a -> a -- | Add a refinement to a type without checking the associated predicate.
src/Strongweak/Weaken.hs view
@@ -21,26 +21,15 @@ import Data.List.NonEmpty qualified as NonEmpty import Data.List.NonEmpty ( NonEmpty ) -{- | Transform an @a@ to a @'Weak' a@.--A given strong type @a@ has exactly one associated weak type @'Weak' a@.-Multiple strong types may weaken to the same weak type.--The following laws must hold:-- * @a == b |= 'weaken' a == 'weaken' b@- * round-trip: @'strengthen' ('weaken' a) == 'pure' a@+{- | Weaken some @a@, relaxing certain invariants. -Most instances should strip an invariant, and not have a recursive context. Some-types don't have an invariant-/either/ handle an invariant, or decompose. See "Strongweak"-for a discussion on this design.+See "Strongweak" for class design notes and laws. -} class Weaken a where- -- | The type to weaken to.+ -- | The weakened type for some type. type Weak a :: Type - -- | Transform a strong value to its associated weak one.+ -- | Weaken some @a@ to its associated weak type @'Weak' a@. weaken :: a -> Weak a -- | Lift a function on a weak type to the associated strong type.
strongweak.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: strongweak-version: 0.4.0+version: 0.4.1 synopsis: Convert between strong and weak representations of types description: Please see README.md. category: Data@@ -31,6 +31,7 @@ Strongweak Strongweak.Generic Strongweak.Generic.Strengthen+ Strongweak.Generic.Via Strongweak.Generic.Weaken Strongweak.Strengthen Strongweak.Strengthen.Unsafe