Skip to content

Commit c8a24e8

Browse files
authored
Data.Graph.SCC: store mutually reachable vertices in a non-empty list (#953)
1 parent 823be58 commit c8a24e8

File tree

2 files changed

+71
-22
lines changed

2 files changed

+71
-22
lines changed

containers/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## ???
44

5+
* Breaking changes to `Data.Graph.SCC v`:
6+
* `CyclicSCC [v]` is now not a constructor,
7+
but a bundled pattern synonym for backward compatibility.
8+
* `NECyclicSCC (NonEmpty v)` is a new constructor, maintaining an invariant
9+
that a set of mutually reachable vertices is non-empty.
10+
511
* Remove the `stack.yaml` file. It was extremely stale, and its utility was a
612
bit dubious in a GHC boot package. Closes #938.
713

containers/src/Data/Graph.hs

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
{-# LANGUAGE DeriveDataTypeable #-}
66
{-# LANGUAGE DeriveGeneric #-}
77
{-# LANGUAGE DeriveLift #-}
8+
{-# LANGUAGE PatternSynonyms #-}
89
{-# LANGUAGE StandaloneDeriving #-}
910
{-# LANGUAGE Safe #-}
11+
{-# LANGUAGE TemplateHaskellQuotes #-}
12+
{-# LANGUAGE ViewPatterns #-}
1013
#endif
1114

1215
#include "containers.h"
@@ -74,7 +77,11 @@ module Data.Graph (
7477

7578

7679
-- * Strongly Connected Components
77-
, SCC(..)
80+
, SCC(..
81+
#ifdef __GLASGOW_HASKELL__
82+
, CyclicSCC
83+
#endif
84+
)
7885

7986
-- ** Construction
8087
, stronglyConnComp
@@ -107,6 +114,9 @@ import Data.Tree (Tree(Node), Forest)
107114

108115
-- std interfaces
109116
import Data.Foldable as F
117+
#if MIN_VERSION_base(4,18,0)
118+
import qualified Data.Foldable1 as F1
119+
#endif
110120
import Control.DeepSeq (NFData(rnf))
111121
import Data.Maybe
112122
import Data.Array
@@ -117,14 +127,16 @@ import Data.Array.Unboxed ( UArray )
117127
import qualified Data.Array as UA
118128
#endif
119129
import qualified Data.List as L
130+
import Data.List.NonEmpty (NonEmpty(..))
131+
import qualified Data.List.NonEmpty as NE
120132
import Data.Functor.Classes
121133
#if !MIN_VERSION_base(4,11,0)
122134
import Data.Semigroup (Semigroup (..))
123135
#endif
124136
#ifdef __GLASGOW_HASKELL__
125137
import GHC.Generics (Generic, Generic1)
126138
import Data.Data (Data)
127-
import Language.Haskell.TH.Syntax (Lift)
139+
import Language.Haskell.TH.Syntax (Lift(..))
128140
-- See Note [ Template Haskell Dependencies ]
129141
import Language.Haskell.TH ()
130142
#endif
@@ -139,15 +151,26 @@ default ()
139151
-------------------------------------------------------------------------
140152

141153
-- | Strongly connected component.
142-
data SCC vertex = AcyclicSCC vertex -- ^ A single vertex that is not
143-
-- in any cycle.
144-
| CyclicSCC [vertex] -- ^ A maximal set of mutually
145-
-- reachable vertices.
154+
data SCC vertex
155+
= AcyclicSCC vertex
156+
-- ^ A single vertex that is not in any cycle.
157+
| NECyclicSCC {-# UNPACK #-} !(NonEmpty vertex)
158+
-- ^ A maximal set of mutually reachable vertices.
159+
--
160+
-- @since 0.7.0
146161
deriving ( Eq -- ^ @since 0.5.9
147162
, Show -- ^ @since 0.5.9
148163
, Read -- ^ @since 0.5.9
149164
)
150165

166+
-- | Partial pattern synonym for backward compatibility with @containers < 0.7@.
167+
pattern CyclicSCC :: [vertex] -> SCC vertex
168+
pattern CyclicSCC xs <- NECyclicSCC (NE.toList -> xs) where
169+
CyclicSCC [] = error "CyclicSCC: an argument cannot be an empty list"
170+
CyclicSCC (x : xs) = NECyclicSCC (x :| xs)
171+
172+
{-# COMPLETE AcyclicSCC, CyclicSCC #-}
173+
151174
#ifdef __GLASGOW_HASKELL__
152175
-- | @since 0.5.9
153176
deriving instance Data vertex => Data (SCC vertex)
@@ -158,47 +181,65 @@ deriving instance Generic1 SCC
158181
-- | @since 0.5.9
159182
deriving instance Generic (SCC vertex)
160183

184+
-- There is no instance Lift (NonEmpty v) before template-haskell-2.15.
185+
#if MIN_VERSION_template_haskell(2,15,0)
161186
-- | @since 0.6.6
162187
deriving instance Lift vertex => Lift (SCC vertex)
188+
#else
189+
instance Lift vertex => Lift (SCC vertex) where
190+
lift (AcyclicSCC v) = [| AcyclicSCC v |]
191+
lift (NECyclicSCC (v :| vs)) = [| NECyclicSCC (v :| vs) |]
192+
#endif
193+
163194
#endif
164195

165196
-- | @since 0.5.9
166197
instance Eq1 SCC where
167198
liftEq eq (AcyclicSCC v1) (AcyclicSCC v2) = eq v1 v2
168-
liftEq eq (CyclicSCC vs1) (CyclicSCC vs2) = liftEq eq vs1 vs2
199+
liftEq eq (NECyclicSCC vs1) (NECyclicSCC vs2) = liftEq eq vs1 vs2
169200
liftEq _ _ _ = False
170201
-- | @since 0.5.9
171202
instance Show1 SCC where
172203
liftShowsPrec sp _sl d (AcyclicSCC v) = showsUnaryWith sp "AcyclicSCC" d v
173-
liftShowsPrec _sp sl d (CyclicSCC vs) = showsUnaryWith (const sl) "CyclicSCC" d vs
204+
liftShowsPrec sp sl d (NECyclicSCC vs) = showsUnaryWith (liftShowsPrec sp sl) "NECyclicSCC" d vs
174205
-- | @since 0.5.9
175206
instance Read1 SCC where
176207
liftReadsPrec rp rl = readsData $
177208
readsUnaryWith rp "AcyclicSCC" AcyclicSCC <>
209+
readsUnaryWith (liftReadsPrec rp rl) "NECyclicSCC" NECyclicSCC <>
178210
readsUnaryWith (const rl) "CyclicSCC" CyclicSCC
179211

180212
-- | @since 0.5.9
181213
instance F.Foldable SCC where
182214
foldr c n (AcyclicSCC v) = c v n
183-
foldr c n (CyclicSCC vs) = foldr c n vs
215+
foldr c n (NECyclicSCC vs) = foldr c n vs
216+
217+
#if MIN_VERSION_base(4,18,0)
218+
-- | @since 0.7.0
219+
instance F1.Foldable1 SCC where
220+
foldMap1 f (AcyclicSCC v) = f v
221+
foldMap1 f (NECyclicSCC vs) = F1.foldMap1 f vs
222+
-- TODO define more methods
223+
#endif
184224

185225
-- | @since 0.5.9
186226
instance Traversable SCC where
187-
-- We treat the non-empty cyclic case specially to cut one
188-
-- fmap application.
189227
traverse f (AcyclicSCC vertex) = AcyclicSCC <$> f vertex
190-
traverse _f (CyclicSCC []) = pure (CyclicSCC [])
191-
traverse f (CyclicSCC (x : xs)) =
192-
liftA2 (\x' xs' -> CyclicSCC (x' : xs')) (f x) (traverse f xs)
228+
-- Avoid traverse from instance Traversable NonEmpty,
229+
-- it is redundantly lazy.
230+
traverse f (NECyclicSCC (x :| xs)) =
231+
liftA2 (\x' xs' -> NECyclicSCC (x' :| xs')) (f x) (traverse f xs)
193232

194233
instance NFData a => NFData (SCC a) where
195234
rnf (AcyclicSCC v) = rnf v
196-
rnf (CyclicSCC vs) = rnf vs
235+
rnf (NECyclicSCC vs) = rnf vs
197236

198237
-- | @since 0.5.4
199238
instance Functor SCC where
200239
fmap f (AcyclicSCC v) = AcyclicSCC (f v)
201-
fmap f (CyclicSCC vs) = CyclicSCC (fmap f vs)
240+
-- Avoid fmap from instance Functor NonEmpty,
241+
-- it is redundantly lazy.
242+
fmap f (NECyclicSCC (x :| xs)) = NECyclicSCC (f x :| map f xs)
202243

203244
-- | The vertices of a list of strongly connected components.
204245
flattenSCCs :: [SCC a] -> [a]
@@ -207,7 +248,7 @@ flattenSCCs = concatMap flattenSCC
207248
-- | The vertices of a strongly connected component.
208249
flattenSCC :: SCC vertex -> [vertex]
209250
flattenSCC (AcyclicSCC v) = [v]
210-
flattenSCC (CyclicSCC vs) = vs
251+
flattenSCC (NECyclicSCC vs) = NE.toList vs
211252

212253
-- | \(O((V+E) \log V)\). The strongly connected components of a directed graph,
213254
-- reverse topologically sorted.
@@ -229,7 +270,8 @@ stronglyConnComp edges0
229270
= map get_node (stronglyConnCompR edges0)
230271
where
231272
get_node (AcyclicSCC (n, _, _)) = AcyclicSCC n
232-
get_node (CyclicSCC triples) = CyclicSCC [n | (n,_,_) <- triples]
273+
get_node (NECyclicSCC ((n0, _, _) :| triples)) =
274+
NECyclicSCC (n0 :| [n | (n, _, _) <- triples])
233275
{-# INLINABLE stronglyConnComp #-}
234276

235277
-- | \(O((V+E) \log V)\). The strongly connected components of a directed graph,
@@ -258,11 +300,12 @@ stronglyConnCompR edges0
258300
where
259301
(graph, vertex_fn,_) = graphFromEdges edges0
260302
forest = scc graph
261-
decode (Node v []) | mentions_itself v = CyclicSCC [vertex_fn v]
303+
304+
decode (Node v []) | mentions_itself v = NECyclicSCC (vertex_fn v :| [])
262305
| otherwise = AcyclicSCC (vertex_fn v)
263-
decode other = CyclicSCC (dec other [])
264-
where
265-
dec (Node v ts) vs = vertex_fn v : foldr dec vs ts
306+
decode (Node v ts) = NECyclicSCC (vertex_fn v :| foldr dec [] ts)
307+
308+
dec (Node v ts) vs = vertex_fn v : foldr dec vs ts
266309
mentions_itself v = v `elem` (graph ! v)
267310
{-# INLINABLE stronglyConnCompR #-}
268311

0 commit comments

Comments
 (0)