Skip to content

Commit 69a67bc

Browse files
authored
Prepare to release 0.3.1 (#30)
This commit regenerated the CI configuration to use a newer base image. It also cleans up warnings that were causing failures in the GHC CI builds.
1 parent ecb1f63 commit 69a67bc

File tree

9 files changed

+92
-62
lines changed

9 files changed

+92
-62
lines changed

.github/workflows/haskell-ci.yml

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
#
99
# For more information, see https://github.com/haskell-CI/haskell-ci
1010
#
11-
# version: 0.19.20240708
11+
# version: 0.19.20250801
1212
#
13-
# REGENDATA ("0.19.20240708",["github","haggle.cabal"])
13+
# REGENDATA ("0.19.20250801",["github","haggle.cabal"])
1414
#
1515
name: Haskell-CI
1616
on:
@@ -19,7 +19,7 @@ on:
1919
jobs:
2020
linux:
2121
name: Haskell-CI - Linux - ${{ matrix.compiler }}
22-
runs-on: ubuntu-20.04
22+
runs-on: ubuntu-24.04
2323
timeout-minutes:
2424
60
2525
container:
@@ -90,15 +90,29 @@ jobs:
9090
allow-failure: false
9191
fail-fast: false
9292
steps:
93-
- name: apt
93+
- name: apt-get install
9494
run: |
9595
apt-get update
9696
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5 libnuma-dev
97+
- name: Install GHCup
98+
run: |
9799
mkdir -p "$HOME/.ghcup/bin"
98-
curl -sL https://downloads.haskell.org/ghcup/0.1.30.0/x86_64-linux-ghcup-0.1.30.0 > "$HOME/.ghcup/bin/ghcup"
100+
curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup"
99101
chmod a+x "$HOME/.ghcup/bin/ghcup"
102+
- name: Install cabal-install
103+
run: |
104+
"$HOME/.ghcup/bin/ghcup" install cabal 3.16.0.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
105+
echo "CABAL=$HOME/.ghcup/bin/cabal-3.16.0.0 -vnormal+nowrap" >> "$GITHUB_ENV"
106+
- name: Install GHC (GHCup)
107+
if: matrix.setup-method == 'ghcup'
108+
run: |
100109
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
101-
"$HOME/.ghcup/bin/ghcup" install cabal 3.12.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
110+
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
111+
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
112+
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
113+
echo "HC=$HC" >> "$GITHUB_ENV"
114+
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
115+
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
102116
env:
103117
HCKIND: ${{ matrix.compilerKind }}
104118
HCNAME: ${{ matrix.compiler }}
@@ -109,21 +123,12 @@ jobs:
109123
echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
110124
echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
111125
echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
112-
HCDIR=/opt/$HCKIND/$HCVER
113-
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
114-
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
115-
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
116-
echo "HC=$HC" >> "$GITHUB_ENV"
117-
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
118-
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
119-
echo "CABAL=$HOME/.ghcup/bin/cabal-3.12.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
120126
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
121127
echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
122128
echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
123129
echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
124130
echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
125131
echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
126-
echo "GHCJSARITH=0" >> "$GITHUB_ENV"
127132
env:
128133
HCKIND: ${{ matrix.compilerKind }}
129134
HCNAME: ${{ matrix.compiler }}
@@ -198,7 +203,11 @@ jobs:
198203
touch cabal.project.local
199204
echo "packages: ${PKGDIR_haggle}" >> cabal.project
200205
if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo "package haggle" >> cabal.project ; fi
201-
if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods" >> cabal.project ; fi
206+
if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods -Werror=missing-fields" >> cabal.project ; fi
207+
if [ $((HCNUMVER >= 90400)) -ne 0 ] ; then echo "package haggle" >> cabal.project ; fi
208+
if [ $((HCNUMVER >= 90400)) -ne 0 ] ; then echo " ghc-options: -Werror=unused-packages" >> cabal.project ; fi
209+
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then echo "package haggle" >> cabal.project ; fi
210+
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then echo " ghc-options: -Werror=incomplete-patterns -Werror=incomplete-uni-patterns" >> cabal.project ; fi
202211
cat >> cabal.project <<EOF
203212
EOF
204213
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(haggle)$/; }' >> cabal.project.local
@@ -239,8 +248,8 @@ jobs:
239248
rm -f cabal.project.local
240249
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
241250
- name: save cache
242-
uses: actions/cache/save@v4
243251
if: always()
252+
uses: actions/cache/save@v4
244253
with:
245254
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
246255
path: ~/.cabal/store

ChangeLog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
0.3.1 (2025-08-13)
2+
------------------
3+
4+
- Update bounds for compatibility with ghc-9.10
5+
- Add `hasCycle` for detecting cycles in graphs (@kquick)
6+
- Optimize DFS algorithms by removing an unnecessary traversal (@kquick)
7+
18
0.3 (2023-08-20)
29
----------------
310

bench/HaggleBench.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE FlexibleContexts #-}
22
{-# LANGUAGE TupleSections #-}
33
{-# LANGUAGE TypeFamilies #-}
4+
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
45

56
module Main ( main ) where
67

haggle.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: haggle
2-
version: 0.3
2+
version: 0.3.1
33
synopsis: A graph library offering mutable, immutable, and inductive graphs
44
description: This library provides mutable (in ST or IO), immutable, and inductive graphs.
55
There are multiple graphs implementations provided to support different use

src/Data/Graph/Haggle/Algorithms/Dominators.hs

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ type Preds = Vector [Int]
5151
immediateDominators :: (Graph g) => g -> Vertex -> [(Vertex, Vertex)]
5252
immediateDominators g root = fromMaybe [] $ do
5353
(res, toNode, _) <- domWork g root
54-
return $ tail $ V.toList $ V.imap (\i n -> (toNode!i, toNode!n)) res
54+
case V.toList $ V.imap (\i n -> (toNode!i, toNode!n)) res of
55+
[] -> error "Impossible: a vertex always dominates itself"
56+
_ : rest -> return rest
5557

5658
-- | Compute all of the dominators for each 'Vertex' reachable from the @root@.
5759
-- Each reachable 'Vertex' is paired with the list of nodes that dominate it,
@@ -66,38 +68,43 @@ dominators g root = fromMaybe [] $ do
6668
[(n, verts) | n <- rest]
6769

6870
domWork :: (Graph g) => g -> Vertex -> Maybe (IDom, ToNode, FromNode)
69-
domWork g root
70-
| null trees = Nothing
71-
| otherwise = return (idom, toNode, fromNode)
71+
domWork g root =
72+
-- Build up a depth-first tree from the root as a first approximation
73+
case dff g [root] of
74+
[] -> Nothing
75+
[tree] ->
76+
let (s, ntree) = numberTree 0 tree
77+
-- Start with an approximation (idom0) where the idom of each node is
78+
-- its parent in the depth-first tree. Note that index 0 is the root,
79+
-- which we will basically be ignoring (since it has no dominator).
80+
dom0Map = M.fromList (treeEdges (-1) ntree)
81+
idom0 = V.generate (M.size dom0Map) (dom0Map M.!)
82+
-- Build a mapping from graph vertices to internal indices. @treeNodes@
83+
-- are nodes that are in the depth-first tree from the root. @otherNodes@
84+
-- are the rest of the nodes in the graph, mapped to -1 (since they aren't
85+
-- going to be in the result)
86+
treeNodes = M.fromList $ zip (T.flatten tree) (T.flatten ntree)
87+
otherNodes = M.fromList $ zip vlist (repeat (-1))
88+
fromNode = M.unionWith const treeNodes otherNodes
89+
90+
-- Translate from internal nodes back to graph nodes (only need the nodes
91+
-- in the depth-first tree)
92+
toNodeMap = M.fromList $ zip (T.flatten ntree) (T.flatten tree)
93+
toNode = V.generate (M.size toNodeMap) (toNodeMap M.!)
94+
95+
-- Use a pre-pass over the graph to collect predecessors so that we don't
96+
-- require a Bidirectional graph. We need a linear pass over the graph
97+
-- here anyway, so we don't lose anything.
98+
predMap = fmap S.toList $ foldr (toPredecessor g) M.empty vlist
99+
preds = V.fromList $ [0] : [filter (/= -1) (map (fromNode M.!) (predMap M.! (toNode ! i)))
100+
| i <- [1..s-1]]
101+
102+
103+
idom = fixEq (refineIDom preds) idom0
104+
in return (idom, toNode, fromNode)
105+
_trees -> error "Impossible: only a single tree can be reachable starting from a single root node"
72106
where
73107
vlist = reachable root g
74-
-- Build up a depth-first tree from the root as a first approximation
75-
trees@(~[tree]) = dff g [root]
76-
(s, ntree) = numberTree 0 tree
77-
-- Start with an approximation (idom0) where the idom of each node is
78-
-- its parent in the depth-first tree. Note that index 0 is the root,
79-
-- which we will basically be ignoring (since it has no dominator).
80-
dom0Map = M.fromList (treeEdges (-1) ntree)
81-
idom0 = V.generate (M.size dom0Map) (dom0Map M.!)
82-
-- Build a mapping from graph vertices to internal indices. @treeNodes@
83-
-- are nodes that are in the depth-first tree from the root. @otherNodes@
84-
-- are the rest of the nodes in the graph, mapped to -1 (since they aren't
85-
-- going to be in the result)
86-
treeNodes = M.fromList $ zip (T.flatten tree) (T.flatten ntree)
87-
otherNodes = M.fromList $ zip vlist (repeat (-1))
88-
fromNode = M.unionWith const treeNodes otherNodes
89-
-- Translate from internal nodes back to graph nodes (only need the nodes
90-
-- in the depth-first tree)
91-
toNodeMap = M.fromList $ zip (T.flatten ntree) (T.flatten tree)
92-
toNode = V.generate (M.size toNodeMap) (toNodeMap M.!)
93-
94-
-- Use a pre-pass over the graph to collect predecessors so that we don't
95-
-- require a Bidirectional graph. We need a linear pass over the graph
96-
-- here anyway, so we don't lose anything.
97-
predMap = fmap S.toList $ foldr (toPredecessor g) M.empty vlist
98-
preds = V.fromList $ [0] : [filter (/= -1) (map (fromNode M.!) (predMap M.! (toNode ! i)))
99-
| i <- [1..s-1]]
100-
idom = fixEq (refineIDom preds) idom0
101108

102109
toPredecessor :: (Graph g)
103110
=> g

src/Data/Graph/Haggle/Classes.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ class (Graph g, HasEdgeLabel g, HasVertexLabel g) => InductiveGraph g where
219219
return g'
220220

221221
addEdgeLabel :: (HasEdgeLabel g) => g -> Edge -> (Edge, EdgeLabel g)
222-
addEdgeLabel g e = (e, el)
223-
where
224-
Just el = edgeLabel g e
222+
addEdgeLabel g e =
223+
case edgeLabel g e of
224+
Just el -> (e, el)
225+
Nothing -> error "Expected an edge label for a graph implementation satisfying the HasEdgeLabel constraint"

src/Data/Graph/Haggle/Internal/Adapter.hs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,17 +324,19 @@ labeledVertices :: (I.Graph g) => LabeledGraph g nl el -> [(I.Vertex, nl)]
324324
labeledVertices g = map toLabVert $ I.vertices (rawGraph g)
325325
where
326326
toLabVert v =
327-
let Just lab = vertexLabel g v
328-
in (v, lab)
327+
case vertexLabel g v of
328+
Just lab -> (v, lab)
329+
Nothing -> error "Impossible: LabeledGraphs always have vertex labels"
329330

330331
-- | Likewise, we use 'edges' here instead of directly reading from the edge
331332
-- label storage array.
332333
labeledEdges :: (I.Graph g) => LabeledGraph g nl el -> [(I.Edge, el)]
333334
labeledEdges g = map toLabEdge $ I.edges (rawGraph g)
334335
where
335336
toLabEdge e =
336-
let Just lab = edgeLabel g e
337-
in (e, lab)
337+
case edgeLabel g e of
338+
Just lab -> (e, lab)
339+
Nothing -> error "Impossible: LabeledGraphs always have edge labels"
338340

339341
mapEdgeLabel :: LabeledGraph g nl el -> (el -> el') -> LabeledGraph g nl el'
340342
mapEdgeLabel g f = g { edgeLabelStore = V.map f (edgeLabelStore g) }

src/Data/Graph/Haggle/PatriciaTree.hs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,10 @@ instance I.HasEdgeLabel (PatriciaTree nl el) where
7979
labeledEdges gr = map toLabEdge (I.edges gr)
8080
where
8181
toLabEdge e =
82-
let Just lab = I.edgeLabel gr e
83-
in (e, lab)
82+
case I.edgeLabel gr e of
83+
Just lab -> (e, lab)
84+
Nothing -> error "Impossible: PatriciaTree instances always have edge labels"
85+
8486
labeledOutEdges (Gr g) (I.V s) = fromMaybe [] $ do
8587
Ctx _ _ _ ss <- IM.lookup s g
8688
return $ IM.foldrWithKey toOut [] ss
@@ -95,8 +97,9 @@ instance I.HasVertexLabel (PatriciaTree nl el) where
9597
labeledVertices gr = map toLabVert (I.vertices gr)
9698
where
9799
toLabVert v =
98-
let Just l = I.vertexLabel gr v
99-
in (v, l)
100+
case I.vertexLabel gr v of
101+
Just l -> (v, l)
102+
Nothing -> error "Impossible: PatriciaTree instances always have vertex labels"
100103

101104
instance I.Bidirectional (PatriciaTree nl el) where
102105
predecessors (Gr g) (I.V v) = fromMaybe [] $ do

tests/GraphTests.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
{-# LANGUAGE FlexibleContexts #-}
33
{-# LANGUAGE FlexibleInstances #-}
44
{-# LANGUAGE TypeFamilies #-}
5+
{-# LANGUAGE TypeOperators #-}
56
{-# LANGUAGE UndecidableInstances #-}
67
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
8+
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
79

810
-- | This module tests Haggle by comparing its results to those of FGL.
911
-- This assumes that FGL is reasonably correct.
@@ -25,8 +27,6 @@ import Control.Arrow ( first, second )
2527
import qualified Data.Bifunctor as Bi
2628
import Control.Monad ( replicateM )
2729
import Data.Function ( on )
28-
import Control.Monad.ST
29-
import Control.Monad ( liftM, filterM, replicateM )
3030
import qualified Data.Foldable as F
3131
import qualified Data.List as L
3232
import Data.Maybe ( fromJust, isNothing )

0 commit comments

Comments
 (0)