Skip to content

Commit 44719da

Browse files
committed
Cleanup and avoid GHC bugs
- Do explicit export lists. This is a chore now, but makes managing Compatibility easier later. - Make `MapWithMove`'s `From` a proper newtype. This improves the compatibility and type error situation. - Work around GHC export list bug: the explicit export lists without big reexports this this for us, in fact.
1 parent 220acfc commit 44719da

File tree

4 files changed

+235
-114
lines changed

4 files changed

+235
-114
lines changed

ChangeLog.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
## Unreleased
44

55
* Rewrite `PatchMapWithMove` in terms of `PatchMapWithPatchingMove`.
6-
Care is taken to make this as little of a breaking change as possible.
7-
In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove` as is the `NodeInfo` of `PatchMapWithPatchingMove`'s `NodeInfo`.
6+
Care is taken to make this not a breaking change.
7+
In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove`, as is the `NodeInfo` and `From` of `PatchMapWithPatchingMove`'s versions of those.
8+
There are complete constructor and field patterns too, and everything is
9+
exported under the newtype as real constructors and fields would be.
810

911
## 0.0.4.0
1012

src/Data/Patch/Class.hs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
23
{-# LANGUAGE TypeFamilies #-}
34
-- | The interface for types which represent changes made to other types
45
module Data.Patch.Class where
56

67
import Data.Functor.Identity
8+
import Data.Kind (Type)
79
import Data.Maybe
810
#if !MIN_VERSION_base(4,11,0)
911
import Data.Semigroup (Semigroup(..))
@@ -15,7 +17,7 @@ import Data.Proxy
1517
-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
1618
-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
1719
class Patch p where
18-
type PatchTarget p :: *
20+
type PatchTarget p :: Type
1921
-- | Apply the patch @p a@ to the value @a@. If no change is needed, return
2022
-- 'Nothing'.
2123
apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
@@ -30,7 +32,7 @@ instance Patch (Identity a) where
3032
apply (Identity a) _ = Just a
3133

3234
-- | 'Proxy' can be used as a 'Patch' that does nothing.
33-
instance Patch (Proxy (a :: *)) where
35+
instance forall (a :: Type). Patch (Proxy a) where
3436
type PatchTarget (Proxy a) = a
3537
apply ~Proxy _ = Nothing
3638

src/Data/Patch/MapWithMove.hs

Lines changed: 128 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
{-# LANGUAGE PatternGuards #-}
99
{-# LANGUAGE PatternSynonyms #-}
1010
{-# LANGUAGE ScopedTypeVariables #-}
11+
{-# LANGUAGE StandaloneDeriving #-}
1112
{-# LANGUAGE TemplateHaskell #-}
1213
{-# LANGUAGE TypeApplications #-}
1314
{-# LANGUAGE TypeFamilies #-}
@@ -16,30 +17,59 @@
1617
-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
1718
-- another
1819
module Data.Patch.MapWithMove
19-
( module Data.Patch.MapWithMove
20-
, PatchMapWithMove
20+
( PatchMapWithMove
2121
( PatchMapWithMove
2222
, -- | Extract the representation of the 'PatchMapWithMove' as a map of
2323
-- 'NodeInfo'.
2424
unPatchMapWithMove
2525
, ..
2626
)
27+
, patchMapWithMove
28+
, patchMapWithMoveInsertAll
29+
, insertMapKey
30+
, moveMapKey
31+
, swapMapKey
32+
, deleteMapKey
33+
, unsafePatchMapWithMove
34+
, patchMapWithMoveNewElements
35+
, patchMapWithMoveNewElementsMap
36+
, patchThatSortsMapWith
37+
, patchThatChangesAndSortsMapWith
38+
, patchThatChangesMap
39+
40+
-- * Node Info
2741
, NodeInfo
2842
( NodeInfo
2943
, _nodeInfo_to
3044
, _nodeInfo_from
3145
, ..
3246
)
33-
-- TODO export these under the type consructor once GHC is fixed
34-
-- , From (From_Insert, From_Delete, From_Move)
47+
, bitraverseNodeInfo
48+
, nodeInfoMapFrom
49+
, nodeInfoMapMFrom
50+
, nodeInfoSetTo
51+
52+
-- * From
53+
, From
54+
( From_Insert
55+
, From_Delete
56+
, From_Move
57+
, ..
58+
)
59+
, bitraverseFrom
60+
61+
-- * To
62+
, To
3563
) where
3664

3765
import Data.Coerce
66+
import Data.Kind (Type)
3867
import Data.Patch.Class
3968
import Data.Patch.MapWithPatchingMove
4069
( PatchMapWithPatchingMove (..)
4170
)
4271
import qualified Data.Patch.MapWithPatchingMove as PM
72+
import Data.Patch.MapWithPatchingMove (To) -- already a transparent synonym
4373

4474
import Control.Lens hiding (from, to)
4575
import Data.List
@@ -54,7 +84,7 @@ import Data.Traversable (foldMapDefault)
5484
-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
5585
-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
5686
-- and vice versa. There should never be any unpaired From/To keys.
57-
newtype PatchMapWithMove k v = PatchMapWithMove'
87+
newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
5888
{ -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
5989
unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
6090
}
@@ -66,10 +96,14 @@ newtype PatchMapWithMove k v = PatchMapWithMove'
6696
, Monoid
6797
)
6898

99+
pattern Coerce :: Coercible a b => a -> b
100+
pattern Coerce x <- (coerce -> x)
101+
where Coerce x = coerce x
102+
69103
{-# COMPLETE PatchMapWithMove #-}
70104
pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
71-
pattern PatchMapWithMove { unPatchMapWithMove } <- PatchMapWithMove' (PatchMapWithPatchingMove (coerce -> unPatchMapWithMove))
72-
where PatchMapWithMove m = PatchMapWithMove' $ PatchMapWithPatchingMove $ coerce m
105+
unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
106+
pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
73107

74108
_PatchMapWithMove
75109
:: Iso
@@ -100,55 +134,6 @@ instance TraversableWithIndex k (PatchMapWithMove k) where
100134
itraversed <.
101135
traverse
102136

103-
newtype NodeInfo k v = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
104-
deriving ( Show, Read, Eq, Ord
105-
)
106-
107-
{-# COMPLETE NodeInfo #-}
108-
pattern NodeInfo :: To k -> From k v -> NodeInfo k v
109-
pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } =
110-
NodeInfo' (PM.NodeInfo
111-
{ PM._nodeInfo_to = _nodeInfo_to
112-
, PM._nodeInfo_from = _nodeInfo_from
113-
})
114-
115-
_NodeInfo
116-
:: Iso
117-
(NodeInfo k0 v0)
118-
(NodeInfo k1 v1)
119-
(PM.NodeInfo k0 (Proxy v0))
120-
(PM.NodeInfo k1 (Proxy v1))
121-
_NodeInfo = iso unNodeInfo' NodeInfo'
122-
123-
instance Functor (NodeInfo k) where
124-
fmap f = runIdentity . traverse (Identity . f)
125-
126-
instance Foldable (NodeInfo k) where
127-
foldMap = foldMapDefault
128-
129-
instance Traversable (NodeInfo k) where
130-
traverse = _NodeInfo . traverseNodeInfo
131-
where
132-
traverseNodeInfo
133-
:: Traversal (PM.NodeInfo k (Proxy a)) (PM.NodeInfo k (Proxy b)) a b
134-
traverseNodeInfo = PM.bitraverseNodeInfo pure (\ ~Proxy -> pure Proxy)
135-
136-
type From k v = PM.From k (Proxy v)
137-
138-
{-# COMPLETE From_Insert, From_Delete, From_Move #-}
139-
140-
pattern From_Insert :: v -> From k v
141-
pattern From_Insert v = PM.From_Insert v
142-
143-
pattern From_Delete :: From k v
144-
pattern From_Delete = PM.From_Delete
145-
146-
pattern From_Move :: k -> From k v
147-
pattern From_Move k = PM.From_Move k Proxy
148-
149-
type To k = PM.To k
150-
151-
152137
-- | Create a 'PatchMapWithMove', validating it
153138
patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
154139
patchMapWithMove = fmap coerce . PM.patchMapWithPatchingMove . coerce
@@ -224,21 +209,106 @@ patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove
224209
patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
225210
PM.patchThatChangesMap oldByIndex newByIndex
226211

212+
--
213+
-- NodeInfo
214+
--
215+
216+
-- | Holds the information about each key: where its new value should come from,
217+
-- and where its old value should go to
218+
newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
219+
220+
deriving instance (Show k, Show p) => Show (NodeInfo k p)
221+
deriving instance (Read k, Read p) => Read (NodeInfo k p)
222+
deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
223+
deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
224+
225+
{-# COMPLETE NodeInfo #-}
226+
pattern NodeInfo :: To k -> From k v -> NodeInfo k v
227+
_nodeInfo_to :: NodeInfo k v -> To k
228+
_nodeInfo_from :: NodeInfo k v -> From k v
229+
pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } =
230+
NodeInfo' (PM.NodeInfo
231+
{ PM._nodeInfo_to = _nodeInfo_to
232+
, PM._nodeInfo_from = Coerce _nodeInfo_from
233+
})
234+
235+
_NodeInfo
236+
:: Iso
237+
(NodeInfo k0 v0)
238+
(NodeInfo k1 v1)
239+
(PM.NodeInfo k0 (Proxy v0))
240+
(PM.NodeInfo k1 (Proxy v1))
241+
_NodeInfo = iso unNodeInfo' NodeInfo'
242+
243+
instance Functor (NodeInfo k) where
244+
fmap f = runIdentity . traverse (Identity . f)
245+
246+
instance Foldable (NodeInfo k) where
247+
foldMap = foldMapDefault
248+
249+
instance Traversable (NodeInfo k) where
250+
traverse = _NodeInfo . traverseNodeInfo
251+
where
252+
traverseNodeInfo
253+
:: Traversal (PM.NodeInfo k (Proxy a)) (PM.NodeInfo k (Proxy b)) a b
254+
traverseNodeInfo = PM.bitraverseNodeInfo pure (\ ~Proxy -> pure Proxy)
255+
256+
bitraverseNodeInfo
257+
:: Applicative f
258+
=> (k0 -> f k1)
259+
-> (v0 -> f v1)
260+
-> NodeInfo k0 v0 -> f (NodeInfo k1 v1)
261+
bitraverseNodeInfo fk fv = fmap coerce
262+
. PM.bitraverseNodeInfo fk (\ ~Proxy -> pure Proxy) fv
263+
. coerce
264+
227265
-- | Change the 'From' value of a 'NodeInfo'
228266
nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
229-
nodeInfoMapFrom = coerce . PM.nodeInfoMapFrom
267+
nodeInfoMapFrom f = coerce . PM.nodeInfoMapFrom (unFrom' . f . From') . coerce
230268

231269
-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
232270
-- 'Applicative', 'Monad', etc.) action to get the new value
233271
nodeInfoMapMFrom
234272
:: Functor f
235273
=> (From k v -> f (From k v))
236274
-> NodeInfo k v -> f (NodeInfo k v)
237-
nodeInfoMapMFrom f = fmap coerce . PM.nodeInfoMapMFrom f . coerce
275+
nodeInfoMapMFrom f = fmap coerce
276+
. PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
277+
. coerce
238278

239279
-- | Set the 'To' field of a 'NodeInfo'
240280
nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
241281
nodeInfoSetTo = coerce . PM.nodeInfoSetTo
242282

283+
--
284+
-- From
285+
--
286+
287+
-- | Describe how a key's new value should be produced
288+
newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
289+
290+
{-# COMPLETE From_Insert, From_Delete, From_Move #-}
291+
292+
-- | Insert the given value here
293+
pattern From_Insert :: v -> From k v
294+
pattern From_Insert v = From' (PM.From_Insert v)
295+
296+
-- | Delete the existing value, if any, from here
297+
pattern From_Delete :: From k v
298+
pattern From_Delete = From' (PM.From_Delete)
299+
300+
-- | Move the value here from the given key
301+
pattern From_Move :: k -> From k v
302+
pattern From_Move k = From' (PM.From_Move k Proxy)
303+
304+
bitraverseFrom
305+
:: Applicative f
306+
=> (k0 -> f k1)
307+
-> (v0 -> f v1)
308+
-> From k0 v0 -> f (From k1 v1)
309+
bitraverseFrom fk fv = fmap coerce
310+
. PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
311+
. coerce
312+
243313
makeWrapped ''PatchMapWithMove
244314
makeWrapped ''NodeInfo

0 commit comments

Comments
 (0)