strongweak 0.11.0 → 0.12.0
raw patch · 9 files changed
+128/−114 lines, 9 files
Files
- CHANGELOG.md +7/−0
- src/Strongweak.hs +4/−3
- src/Strongweak/Chain.hs +45/−0
- src/Strongweak/Strength.hs +0/−6
- src/Strongweak/Strengthen.hs +28/−1
- src/Strongweak/Weaken.hs +42/−8
- src/Strongweak/WeakenN.hs +0/−38
- src/Strongweak/WeakenN/Internal.hs +0/−55
- strongweak.cabal +2/−3
CHANGELOG.md view
@@ -1,3 +1,10 @@+## 0.12.0 (2025-03-29)+* rename `WeakenN` to `SWChain`+* remove `SWN`, don't like+* export previous `SWChain` internals `weakenN` and `strengthenN` for magical+ strongweak chaining (!)+ * no need to write `weaken . weaken . weaken` when `weakenN @3` will do!+ ## 0.11.0 (2024-10-17) * add `WeakenN` and `SWN` for chaining weakenings * clarify instance design: even zero-invariant coercible newtypes aren't allowed
src/Strongweak.hs view
@@ -10,24 +10,25 @@ -- * Classes Weaken(..)+ , weakenN , Strengthen(..)+ , strengthenN -- * Other definitions- , WeakenN(..)+ , SWChain(..) , SWCoercibly(..) , liftWeakF -- * Strength switch wrapper , Strength(..) , type SW- , type SWN ) where import Strongweak.Weaken import Strongweak.Strengthen import Strongweak.Strength-import Strongweak.WeakenN+import Strongweak.Chain {- $strongweak-instance-design
+ src/Strongweak/Chain.hs view
@@ -0,0 +1,45 @@+module Strongweak.Chain ( SWChain(..), strengthenN ) where++import Strongweak.Weaken ( Weaken(..), WeakenN(weakenN), type WeakenedN )+import Strongweak.Strengthen ( Strengthen(..), StrengthenN(strengthenN) )+import GHC.TypeNats ( type Natural )++{- | When weakening (or strengthening), chain the operation @n@ times.++You may achieve this without extra newtypes by nesting uses of+'Strongweak.Strength.SW'. However, strongweak generics can't handle this,+forcing you to write manual instances.++'SWChain' provides this nesting behaviour in a type. In return for adding a+boring newtype layer to the strong representation, you can chain weakening and+strengthenings without having to write them manually.++The type works as follows:++@+'Weakened' ('SWChain' 0 a) = a+'Weakened' ('SWChain' 1 a) = 'Weakened' a+'Weakened' ('SWChain' 2 a) = 'Weakened' ('Weakened' a)+'Weakened' ('SWChain' n a) = 'WeakenedN' n a+@++And so on. (This type is only much use from @n = 2@ onwards.)++You may also use this as a "via" type:++@+newtype A (s :: 'Strength') = A { a1 :: 'SW' s (Identity ('SW' s Word8)) }+deriving via 'SWChain' 2 (Identity Word8) instance 'Weaken' (A 'Strong')+deriving via 'SWChain' 2 (Identity Word8) instance 'Strengthen' (A 'Strong')+@+-}+newtype SWChain (n :: Natural) a = SWChain { unSWChain :: a }+ deriving stock Show+ deriving (Ord, Eq) via a++instance WeakenN n a => Weaken (SWChain n a) where+ type Weakened (SWChain n a) = WeakenedN n a+ weaken = weakenN @n . unSWChain++instance StrengthenN n a => Strengthen (SWChain n a) where+ strengthen = fmap SWChain . strengthenN @n
src/Strongweak/Strength.hs view
@@ -5,9 +5,6 @@ import Strongweak.Weaken ( type Weakened ) import Data.Kind ( type Type ) -import Strongweak.WeakenN-import GHC.TypeNats ( type Natural )- -- | Strength enumeration: it's either strong, or weak. -- -- Primarily interesting at the type level (using DataKinds).@@ -29,6 +26,3 @@ type family SW (s :: Strength) a :: Type where SW Strong a = a SW Weak a = Weakened a---- | Shortcut for a 'SW'-wrapped 'WeakenN'.-type SWN (s :: Strength) (n :: Natural) a = SW s (WeakenN n a)
src/Strongweak/Strengthen.hs view
@@ -1,11 +1,14 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE AllowAmbiguousTypes #-} -- StrengthenN, and typeRep'+-- StrengthenN type families in constraints+{-# LANGUAGE UndecidableInstances #-} module Strongweak.Strengthen ( -- * 'Strengthen' class Strengthen(..) , restrengthen+ , StrengthenN(strengthenN) -- ** Helpers , strengthenBounded@@ -46,6 +49,11 @@ import Data.Typeable ( Typeable, TypeRep, typeRep, Proxy(Proxy) ) +-- for strengthenN+import GHC.TypeNats ( type Natural, type (-) )+import Strongweak.Weaken ( type WeakenedN, WeakenN )+import Unsafe.Coerce ( unsafeCoerce )+ {- | Attempt to strengthen some @'Weakened' a@, asserting certain invariants. We take 'Weaken' as a superclass in order to maintain strong/weak type pair@@ -250,3 +258,22 @@ typeRep' :: forall a. Typeable a => TypeRep typeRep' = typeRep (Proxy @a)++class WeakenN n a => StrengthenN (n :: Natural) a where+ strengthenN :: WeakenedN n a -> Either StrengthenFailure' a++instance {-# OVERLAPPING #-} StrengthenN 0 a where+ strengthenN = Right++instance (Strengthen a, StrengthenN (n-1) (Weakened a))+ => StrengthenN n a where+ strengthenN a =+ case strengthenN @(n-1) @(Weakened a) (weakenedNLR1 @n @a a) of+ Left e -> Left e+ Right sa -> strengthen sa++-- | Inductive 'WeakenedN' case.+--+-- @n@ must not be 0.+weakenedNLR1 :: forall n a. WeakenedN n a -> WeakenedN (n-1) (Weakened a)+weakenedNLR1 = unsafeCoerce
src/Strongweak/Weaken.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE UndecidableInstances #-} -- for WeakenedN+{-# LANGUAGE AllowAmbiguousTypes #-} -- for weakenN module Strongweak.Weaken ( Weaken(Weakened, weaken) , type WeakenedN+ , WeakenN(weakenN) , liftWeakF , SWCoercibly(..) ) where@@ -20,6 +22,8 @@ import Data.List.NonEmpty ( NonEmpty ) import GHC.TypeNats +import Unsafe.Coerce ( unsafeCoerce )+ {- | Weaken some @a@, relaxing certain invariants. See "Strongweak" for class design notes and laws.@@ -41,14 +45,24 @@ WeakenedN 0 a = a WeakenedN n a = Weakened (WeakenedN (n-1) a) --- | A "via type" newtype for defining strongweak instances for zero-invariant,--- coercible newtypes.------ Use like so:------ @--- deriving via 'SWCoercibly' a instance 'Weaken' ('Identity' a)--- @+{- | A "via type" newtype for defining strongweak instances for zero-invariant,+ coercible newtypes.++Use like so:++@+deriving 'Weaken' via 'SWCoercibly' a+@++Or standalone:++@+via 'SWCoercibly' a instance 'Weaken' ('Identity' a)+@++Note that usage of this incurs UndecidableInstances. That's life. You can+write the trivial instances this generates yourself if you so wish.+-} newtype SWCoercibly a = SWCoercibly { unSWCoercibly :: a } instance Weaken (SWCoercibly a) where type Weakened (SWCoercibly a) = a@@ -128,3 +142,23 @@ type Weakened (Either a b) = Either (Weakened a) (Weakened b) weaken = \case Left a -> Left $ weaken a Right b -> Right $ weaken b++class WeakenN (n :: Natural) a where+ weakenN :: a -> WeakenedN n a++-- | Zero case: return the value as-is.+instance {-# OVERLAPPING #-} WeakenN 0 a where+ weakenN = id++-- | Inductive case. @n /= 0@, else this explodes.+instance (Weaken a, WeakenN (n-1) (Weakened a))+ => WeakenN n a where+ weakenN a =+ case weakenN @(n-1) @(Weakened a) (weaken a) of+ x -> weakenedNRL1 @n @a x++-- | Inverted inductive 'WeakenedN'case.+--+-- @n@ must not be 0.+weakenedNRL1 :: forall n a. WeakenedN (n-1) (Weakened a) -> WeakenedN n a+weakenedNRL1 = unsafeCoerce
− src/Strongweak/WeakenN.hs
@@ -1,38 +0,0 @@-module Strongweak.WeakenN ( WeakenN(..) ) where--import Strongweak.WeakenN.Internal-import Strongweak.Weaken ( Weaken(..), type WeakenedN )-import Strongweak.Strengthen ( Strengthen(..) )-import GHC.TypeNats ( type Natural )--{- | When weakening (or strengthening), chain the operation @n@ times.--You may achieve this without extra newtypes by nesting uses of-'Strongweak.Weaken.SW'. However, strongweak generics can't handle this, forcing-you to write manual instances.--'WeakenN' provides this nesting behaviour in a type. In return for adding a-boring newtype layer to the strong representation, you can chain weakening and-strengthenings without having to write them manually.--The type works as follows:--@-Weakened (WeakenN 0 a) = a-Weakened (WeakenN 1 a) = Weakened a-Weakened (WeakenN 2 a) = Weakened (Weakened a)-Weakened (WeakenN n a) = WeakenedN n a-@--And so on. (This type is only much use from @n = 2@ onwards.)--}-newtype WeakenN (n :: Natural) a = WeakenN { unWeakenN :: a }- deriving stock Show- deriving (Ord, Eq) via a--instance WeakenWeakenN n a => Weaken (WeakenN n a) where- type Weakened (WeakenN n a) = WeakenedN n a- weaken = weakenWeakenN @n @a . unWeakenN--instance StrengthenWeakenN n a => Strengthen (WeakenN n a) where- strengthen = fmap WeakenN . strengthenWeakenN @n @a
− src/Strongweak/WeakenN/Internal.hs
@@ -1,55 +0,0 @@-{-# LANGUAGE UndecidableInstances #-} -- type family 'Weakened' in constraints-{-# LANGUAGE AllowAmbiguousTypes #-} -- ambiguous intermediate type classes--{- | 'Strongweak.WeakenN.WeakenN' internals.--Just in case. You shouldn't need these, but they might be fun to look at.--__Internal module. Exports may change without warning. Try not to use.__--}--module Strongweak.WeakenN.Internal where--import Strongweak.Weaken ( Weaken(weaken), type WeakenedN )-import Strongweak.Strengthen-import GHC.TypeNats ( type Natural, type (-) )-import Unsafe.Coerce ( unsafeCoerce )--class WeakenWeakenN (n :: Natural) a where- weakenWeakenN :: a -> WeakenedN n a---- | Zero case: return the value as-is.-instance {-# OVERLAPPING #-} WeakenWeakenN 0 a where- weakenWeakenN = id---- | Inductive case. @n /= 0@, else this explodes.-instance (Weaken a, WeakenWeakenN (n-1) (Weakened a))- => WeakenWeakenN n a where- weakenWeakenN a =- case weakenWeakenN @(n-1) @(Weakened a) (weaken a) of- x -> weakenedNRL1 @n @a x---- | Inverted inductive 'WeakenedN'case.------ @n@ must not be 0.-weakenedNRL1 :: forall n a. WeakenedN (n-1) (Weakened a) -> WeakenedN n a-weakenedNRL1 = unsafeCoerce--class WeakenWeakenN n a => StrengthenWeakenN (n :: Natural) a where- strengthenWeakenN :: WeakenedN n a -> Either StrengthenFailure' a--instance {-# OVERLAPPING #-} StrengthenWeakenN 0 a where- strengthenWeakenN = Right--instance (Strengthen a, StrengthenWeakenN (n-1) (Weakened a))- => StrengthenWeakenN n a where- strengthenWeakenN a =- case strengthenWeakenN @(n-1) @(Weakened a) (weakenedNLR1 @n @a a) of- Left e -> Left e- Right sa -> strengthen sa---- | Inductive 'WeakenedN'case.------ @n@ must not be 0.-weakenedNLR1 :: forall n a. WeakenedN n a -> WeakenedN (n-1) (Weakened a)-weakenedNLR1 = unsafeCoerce
strongweak.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: strongweak-version: 0.11.0+version: 0.12.0 synopsis: Convert between strong and weak representations of types description: Please see README.md. category: Data@@ -30,6 +30,7 @@ library exposed-modules: Strongweak+ Strongweak.Chain Strongweak.Generic Strongweak.Strength Strongweak.Strengthen@@ -38,8 +39,6 @@ Strongweak.Util.TypeNats Strongweak.Weaken Strongweak.Weaken.Generic- Strongweak.WeakenN- Strongweak.WeakenN.Internal other-modules: Paths_strongweak hs-source-dirs: