Skip to content

Commit b21d39e

Browse files
committed
remove Tagged, add Coercibly
This appears to be what I want. Given a newtype, I can choose to remove just the newtype layering, or do that and also weaken the inner type. Furthermore, I can recover either option even if an instance already exists, by using `Coercibly` directly. Very interesting! Now we can have more agency over newtype wrappers in strongweak, which was previously a bit problematic, /and/ it potentially solves an orphan instance issue in binrep.
1 parent 0b59ab1 commit b21d39e

File tree

4 files changed

+69
-29
lines changed

4 files changed

+69
-29
lines changed

package.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ dependencies:
6666
- rerefined ^>= 0.8.0
6767
- vector-sized >= 1.5.0 && < 1.7
6868
- vector >= 0.12.3.1 && < 0.14
69-
- tagged ^>= 0.8.8
7069

7170
library:
7271
source-dirs: src

src/Strongweak/Strengthen.hs

+18-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ module Strongweak.Strengthen
2121
) where
2222

2323
import Strongweak.Util.TypeNats ( natVal'' )
24-
import Strongweak.Weaken ( Weaken(Weakened, weaken) )
24+
import Strongweak.Weaken
25+
( Weaken(Weakened, weaken), Coercibly(..), Coercibly1(..), Strategy(..) )
2526

2627
import GHC.TypeNats ( KnownNat )
2728
import Data.Word
@@ -41,7 +42,7 @@ import Data.Bits ( FiniteBits )
4142

4243
import Data.Typeable ( Typeable, TypeRep, typeRep, Proxy(Proxy) )
4344

44-
import Data.Tagged ( Tagged(..) )
45+
import Data.Coerce
4546

4647
{- | Attempt to strengthen some @'Weakened' a@, asserting certain invariants.
4748
@@ -247,7 +248,18 @@ f .> g = g . f
247248
typeRep' :: forall a. Typeable a => TypeRep
248249
typeRep' = typeRep (Proxy @a)
249250

250-
-- | SPECIAL: Strengthen through a 'Tagged'. That is, strengthen @a@ then tag it
251-
-- with @x@.
252-
instance Strengthen a => Strengthen (Tagged x a) where
253-
strengthen = fmap Tagged <$> strengthen
251+
instance Coercible from to => Strengthen (Coercibly Shallow from to) where
252+
strengthen = Right . Coercibly . coerce @to @from
253+
254+
-- TODO wrap errors here?
255+
instance (Coercible from to, Strengthen to)
256+
=> Strengthen (Coercibly Deep from to) where
257+
strengthen = fmap (Coercibly . coerce @to @from) <$> strengthen
258+
259+
instance Coercible (f a) a => Strengthen (Coercibly1 Shallow f a) where
260+
strengthen = Right . Coercibly1 . coerce @a @(f a)
261+
262+
-- TODO wrap errors here?
263+
instance (Coercible (f a) a, Strengthen a)
264+
=> Strengthen (Coercibly1 Deep f a) where
265+
strengthen = fmap (Coercibly1 . coerce @a @(f a)) <$> strengthen

src/Strongweak/Weaken.hs

+51-20
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ module Strongweak.Weaken
1010
, Strength(..)
1111
, type SW
1212
, type SWDepth
13+
14+
, Coercibly(..)
15+
, Coercibly1(..)
16+
, Strategy(..)
1317
) where
1418

1519
import Rerefined
@@ -23,7 +27,8 @@ import Data.Functor.Const
2327
import Data.List.NonEmpty qualified as NonEmpty
2428
import Data.List.NonEmpty ( NonEmpty )
2529
import GHC.TypeNats
26-
import Data.Tagged ( Tagged(..) )
30+
31+
import Data.Coerce
2732

2833
{- | Weaken some @a@, relaxing certain invariants.
2934
@@ -89,15 +94,8 @@ instance VG.Vector v a => Weaken (VGS.Vector v n a) where
8994
type Weakened (VGS.Vector v n a) = [a]
9095
weaken = VGS.toList
9196

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)
10199

102100
{- TODO controversial. seems logical, but also kinda annoying.
103101
-- | Weaken 'Maybe' (0 or 1) into '[]' (0 to n).
@@ -151,13 +149,46 @@ instance (Weaken a, Weaken b) => Weaken (Either a b) where
151149
weaken = \case Left a -> Left $ weaken a
152150
Right b -> Right $ weaken b
153151

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

strongweak.cabal

-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ library
5656
build-depends:
5757
base >=4.18 && <5
5858
, rerefined >=0.8.0 && <0.9
59-
, tagged >=0.8.8 && <0.9
6059
, text >=2.0 && <2.2
6160
, text-builder-linear >=0.1.3 && <0.2
6261
, vector >=0.12.3.1 && <0.14
@@ -94,7 +93,6 @@ test-suite spec
9493
, quickcheck-instances >=0.3.26 && <0.4
9594
, rerefined >=0.8.0 && <0.9
9695
, strongweak
97-
, tagged >=0.8.8 && <0.9
9896
, text >=2.0 && <2.2
9997
, text-builder-linear >=0.1.3 && <0.2
10098
, vector >=0.12.3.1 && <0.14

0 commit comments

Comments
 (0)