@@ -10,6 +10,10 @@ module Strongweak.Weaken
10
10
, Strength (.. )
11
11
, type SW
12
12
, type SWDepth
13
+
14
+ , Coercibly (.. )
15
+ , Coercibly1 (.. )
16
+ , Strategy (.. )
13
17
) where
14
18
15
19
import Rerefined
@@ -23,7 +27,8 @@ import Data.Functor.Const
23
27
import Data.List.NonEmpty qualified as NonEmpty
24
28
import Data.List.NonEmpty ( NonEmpty )
25
29
import GHC.TypeNats
26
- import Data.Tagged ( Tagged (.. ) )
30
+
31
+ import Data.Coerce
27
32
28
33
{- | Weaken some @a@, relaxing certain invariants.
29
34
@@ -89,15 +94,8 @@ instance VG.Vector v a => Weaken (VGS.Vector v n a) where
89
94
type Weakened (VGS. Vector v n a ) = [a ]
90
95
weaken = VGS. toList
91
96
92
- -- | Strip wrapper.
93
- instance Weaken (Identity a ) where
94
- type Weakened (Identity a ) = a
95
- weaken = runIdentity
96
-
97
- -- | Strip wrapper.
98
- instance Weaken (Const a b ) where
99
- type Weakened (Const a b ) = a
100
- weaken = getConst
97
+ deriving via Coercibly1 Shallow Identity a instance Weaken (Identity a )
98
+ deriving via Coercibly Shallow (Const a b ) a instance Weaken (Const a b )
101
99
102
100
{- TODO controversial. seems logical, but also kinda annoying.
103
101
-- | Weaken 'Maybe' (0 or 1) into '[]' (0 to n).
@@ -151,13 +149,46 @@ instance (Weaken a, Weaken b) => Weaken (Either a b) where
151
149
weaken = \ case Left a -> Left $ weaken a
152
150
Right b -> Right $ weaken b
153
151
154
- -- | SPECIAL: Weaken through a 'Tagged'. That is, strip the 'Tagged' and weaken
155
- -- the inner @a@.
156
- --
157
- -- This appears to contribute a useful role: we want to plug some newtype into
158
- -- the strongweak ecosystem, but it would result in orphan instances. With this,
159
- -- we can go through 'Tagged', and the phantom type helps us handle
160
- -- parameterized newtypes (like @newtype 'ByteOrdered' (end :: 'ByteOrder') a@).
161
- instance Weaken a => Weaken (Tagged x a ) where
162
- type Weakened (Tagged x a ) = Weakened a
163
- weaken = weaken . unTagged
152
+ -- note that without the Coercible instance, we get a confusing "couldn't match
153
+ -- representation of type 'from' with that of 'to'" error message. this might
154
+ -- happen in user code that tries to be parametric with 'Coercibly'
155
+
156
+ -- | How to weaken a layer type.
157
+ data Strategy
158
+ = Shallow -- ^ Remove the layer.
159
+ | Deep -- ^ Remove the layer, and weaken the inner type.
160
+
161
+ {- | A @from@ that can be safely coerced between @to@.
162
+
163
+ You can use this to decide precisely how to weaken a newtype: whether to only
164
+ strip the newtype via 'Shallow', or to strip the newtype and weaken the inner
165
+ type via 'Deep'.
166
+ -}
167
+ newtype Coercibly (stg :: Strategy ) (from :: Type ) to
168
+ = Coercibly { unCoercibly :: from }
169
+ deriving stock Show
170
+
171
+ -- | Remove the coercible @from@ layer.
172
+ instance Coercible from to => Weaken (Coercibly Shallow from to ) where
173
+ type Weakened (Coercibly Shallow from to ) = to
174
+ weaken = coerce . unCoercibly
175
+
176
+ -- | Remove the coercible @from@ layer and weaken the result.
177
+ instance (Coercible from to , Weaken to ) => Weaken (Coercibly Deep from to ) where
178
+ type Weakened (Coercibly Deep from to ) = Weakened to
179
+ weaken = weaken . coerce @ from @ to . unCoercibly
180
+
181
+ -- | An @f a@ that can be safely coerced between @a@.
182
+ newtype Coercibly1 (stg :: Strategy ) f (a :: Type )
183
+ = Coercibly1 { unCoercibly1 :: f a }
184
+ deriving stock Show
185
+
186
+ -- | Remove the coercible @f a@ layer.
187
+ instance Coercible (f a ) a => Weaken (Coercibly1 Shallow f a ) where
188
+ type Weakened (Coercibly1 Shallow f a ) = a
189
+ weaken = coerce . unCoercibly1
190
+
191
+ -- | Remove the coercible @f a@ layer and weaken the result.
192
+ instance (Coercible (f a ) a , Weaken a ) => Weaken (Coercibly1 Deep f a ) where
193
+ type Weakened (Coercibly1 Deep f a ) = Weakened a
194
+ weaken = weaken . coerce @ (f a ) @ a . unCoercibly1
0 commit comments