Skip to content

Commit f783d16

Browse files
committed
rename WeakenN -> SWChain, move & export internals
This is so cool :)
1 parent 35831d7 commit f783d16

9 files changed

+111
-101
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.12.0 (unreleased)
2+
* rename `WeakenN` to `SWChain`
3+
* export previous `SWChain` internals `weakenN` and `strengthenN` for magical
4+
strongweak chaining (!)
5+
* no need to write `weaken . weaken . weaken` when `weakenN @3` will do!
6+
17
## 0.11.0 (2024-10-17)
28
* add `WeakenN` and `SWN` for chaining weakenings
39
* clarify instance design: even zero-invariant coercible newtypes aren't allowed

src/Strongweak.hs

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ module Strongweak
1010

1111
-- * Classes
1212
Weaken(..)
13+
, weakenN
1314
, Strengthen(..)
15+
, strengthenN
1416

1517
-- * Other definitions
16-
, WeakenN(..)
18+
, SWChain(..)
1719
, SWCoercibly(..)
1820
, liftWeakF
1921

@@ -27,7 +29,7 @@ module Strongweak
2729
import Strongweak.Weaken
2830
import Strongweak.Strengthen
2931
import Strongweak.Strength
30-
import Strongweak.WeakenN
32+
import Strongweak.Chain
3133

3234
{- $strongweak-instance-design
3335

src/Strongweak/Chain.hs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module Strongweak.Chain ( SWChain(..), strengthenN ) where
2+
3+
import Strongweak.Weaken ( Weaken(..), WeakenN(weakenN), type WeakenedN )
4+
import Strongweak.Strengthen ( Strengthen(..), StrengthenN(strengthenN) )
5+
import GHC.TypeNats ( type Natural )
6+
7+
{- | When weakening (or strengthening), chain the operation @n@ times.
8+
9+
You may achieve this without extra newtypes by nesting uses of
10+
'Strongweak.Strength.SW'. However, strongweak generics can't handle this,
11+
forcing you to write manual instances.
12+
13+
'SWChain' provides this nesting behaviour in a type. In return for adding a
14+
boring newtype layer to the strong representation, you can chain weakening and
15+
strengthenings without having to write them manually.
16+
17+
The type works as follows:
18+
19+
@
20+
'Weakened' ('SWChain' 0 a) = a
21+
'Weakened' ('SWChain' 1 a) = 'Weakened' a
22+
'Weakened' ('SWChain' 2 a) = 'Weakened' ('Weakened' a)
23+
'Weakened' ('SWChain' n a) = 'WeakenedN' n a
24+
@
25+
26+
And so on. (This type is only much use from @n = 2@ onwards.)
27+
28+
You may also use this as a "via" type:
29+
30+
@
31+
newtype A (s :: 'Strength') = A { a1 :: 'SW' s (Identity ('SW' s Word8)) }
32+
deriving via 'SWChain' 2 (Identity Word8) instance 'Weaken' (A 'Strong')
33+
deriving via 'SWChain' 2 (Identity Word8) instance 'Strengthen' (A 'Strong')
34+
@
35+
-}
36+
newtype SWChain (n :: Natural) a = SWChain { unSWChain :: a }
37+
deriving stock Show
38+
deriving (Ord, Eq) via a
39+
40+
instance WeakenN n a => Weaken (SWChain n a) where
41+
type Weakened (SWChain n a) = WeakenedN n a
42+
weaken = weakenN @n . unSWChain
43+
44+
instance StrengthenN n a => Strengthen (SWChain n a) where
45+
strengthen = fmap SWChain . strengthenN @n

src/Strongweak/Strength.hs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Strongweak.Strength where
55
import Strongweak.Weaken ( type Weakened )
66
import Data.Kind ( type Type )
77

8-
import Strongweak.WeakenN
8+
import Strongweak.Chain
99
import GHC.TypeNats ( type Natural )
1010

1111
-- | Strength enumeration: it's either strong, or weak.
@@ -30,5 +30,5 @@ type family SW (s :: Strength) a :: Type where
3030
SW Strong a = a
3131
SW Weak a = Weakened a
3232

33-
-- | Shortcut for a 'SW'-wrapped 'WeakenN'.
34-
type SWN (s :: Strength) (n :: Natural) a = SW s (WeakenN n a)
33+
-- | Shortcut for a 'SW'-wrapped 'SWChain'.
34+
type SWN (s :: Strength) (n :: Natural) a = SW s (SWChain n a)

src/Strongweak/Strengthen.hs

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
{-# LANGUAGE OverloadedStrings #-}
2-
{-# LANGUAGE AllowAmbiguousTypes #-}
2+
{-# LANGUAGE AllowAmbiguousTypes #-} -- StrengthenN, and typeRep'
3+
-- StrengthenN type families in constraints
4+
{-# LANGUAGE UndecidableInstances #-}
35

46
module Strongweak.Strengthen
57
(
68
-- * 'Strengthen' class
79
Strengthen(..)
810
, restrengthen
11+
, StrengthenN(strengthenN)
912

1013
-- ** Helpers
1114
, strengthenBounded
@@ -46,6 +49,11 @@ import Data.Bits ( FiniteBits )
4649

4750
import Data.Typeable ( Typeable, TypeRep, typeRep, Proxy(Proxy) )
4851

52+
-- for strengthenN
53+
import GHC.TypeNats ( type Natural, type (-) )
54+
import Strongweak.Weaken ( type WeakenedN, WeakenN )
55+
import Unsafe.Coerce ( unsafeCoerce )
56+
4957
{- | Attempt to strengthen some @'Weakened' a@, asserting certain invariants.
5058
5159
We take 'Weaken' as a superclass in order to maintain strong/weak type pair
@@ -250,3 +258,22 @@ f .> g = g . f
250258

251259
typeRep' :: forall a. Typeable a => TypeRep
252260
typeRep' = typeRep (Proxy @a)
261+
262+
class WeakenN n a => StrengthenN (n :: Natural) a where
263+
strengthenN :: WeakenedN n a -> Either StrengthenFailure' a
264+
265+
instance {-# OVERLAPPING #-} StrengthenN 0 a where
266+
strengthenN = Right
267+
268+
instance (Strengthen a, StrengthenN (n-1) (Weakened a))
269+
=> StrengthenN n a where
270+
strengthenN a =
271+
case strengthenN @(n-1) @(Weakened a) (weakenedNLR1 @n @a a) of
272+
Left e -> Left e
273+
Right sa -> strengthen sa
274+
275+
-- | Inductive 'WeakenedN'case.
276+
--
277+
-- @n@ must not be 0.
278+
weakenedNLR1 :: forall n a. WeakenedN n a -> WeakenedN (n-1) (Weakened a)
279+
weakenedNLR1 = unsafeCoerce

src/Strongweak/Weaken.hs

+24
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{-# LANGUAGE UndecidableInstances #-} -- for WeakenedN
2+
{-# LANGUAGE AllowAmbiguousTypes #-} -- for weakenN
23

34
module Strongweak.Weaken
45
(
56
Weaken(Weakened, weaken)
67
, type WeakenedN
8+
, WeakenN(weakenN)
79
, liftWeakF
810
, SWCoercibly(..)
911
) where
@@ -20,6 +22,8 @@ import Data.List.NonEmpty qualified as NonEmpty
2022
import Data.List.NonEmpty ( NonEmpty )
2123
import GHC.TypeNats
2224

25+
import Unsafe.Coerce ( unsafeCoerce )
26+
2327
{- | Weaken some @a@, relaxing certain invariants.
2428
2529
See "Strongweak" for class design notes and laws.
@@ -128,3 +132,23 @@ instance (Weaken a, Weaken b) => Weaken (Either a b) where
128132
type Weakened (Either a b) = Either (Weakened a) (Weakened b)
129133
weaken = \case Left a -> Left $ weaken a
130134
Right b -> Right $ weaken b
135+
136+
class WeakenN (n :: Natural) a where
137+
weakenN :: a -> WeakenedN n a
138+
139+
-- | Zero case: return the value as-is.
140+
instance {-# OVERLAPPING #-} WeakenN 0 a where
141+
weakenN = id
142+
143+
-- | Inductive case. @n /= 0@, else this explodes.
144+
instance (Weaken a, WeakenN (n-1) (Weakened a))
145+
=> WeakenN n a where
146+
weakenN a =
147+
case weakenN @(n-1) @(Weakened a) (weaken a) of
148+
x -> weakenedNRL1 @n @a x
149+
150+
-- | Inverted inductive 'WeakenedN'case.
151+
--
152+
-- @n@ must not be 0.
153+
weakenedNRL1 :: forall n a. WeakenedN (n-1) (Weakened a) -> WeakenedN n a
154+
weakenedNRL1 = unsafeCoerce

src/Strongweak/WeakenN.hs

-38
This file was deleted.

src/Strongweak/WeakenN/Internal.hs

-55
This file was deleted.

strongweak.cabal

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ source-repository head
3030
library
3131
exposed-modules:
3232
Strongweak
33+
Strongweak.Chain
3334
Strongweak.Generic
3435
Strongweak.Strength
3536
Strongweak.Strengthen
@@ -38,8 +39,6 @@ library
3839
Strongweak.Util.TypeNats
3940
Strongweak.Weaken
4041
Strongweak.Weaken.Generic
41-
Strongweak.WeakenN
42-
Strongweak.WeakenN.Internal
4342
other-modules:
4443
Paths_strongweak
4544
hs-source-dirs:

0 commit comments

Comments
 (0)