Skip to content

Instantly share code, notes, and snippets.

@tfausak
Last active December 24, 2016 04:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tfausak/98726c8255c5bc5c940e0f4939978a43 to your computer and use it in GitHub Desktop.
Save tfausak/98726c8255c5bc5c940e0f4939978a43 to your computer and use it in GitHub Desktop.
Gets the package index from Hackage and outputs a bunch of information about their version numbers.
{- stack
--resolver lts-7
--install-ghc
runghc
--package containers
--package filepath
--package http-client
--package http-client-tls
--package tar
--package time
--package zlib
--
-Wall
-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Function ((&))
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Compression.GZip as GZip
import qualified Control.Monad as Monad
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import qualified Data.Ord as Ord
import qualified Data.Set as Set
import qualified Data.Time as Time
import qualified Data.Tuple as Tuple
import qualified Data.Version as Version
import qualified Network.HTTP.Client as Client
import qualified Network.HTTP.Client.TLS as TLS
import qualified System.FilePath as FilePath
import qualified Text.ParserCombinators.ReadP as ReadP
main :: IO ()
main = do
now <- Time.getCurrentTime
print now
putStrLn "Getting package index ..."
let url = "https://hackage.haskell.org/packages/index.tar.gz"
request <- Client.parseUrlThrow url
manager <- Client.newManager TLS.tlsManagerSettings
response <- Client.httpLbs request manager
putStrLn "Got package index."
let body = Client.responseBody response
let archive = GZip.decompress body
let entries = Tar.read archive
let packages = Tar.foldEntries update Map.empty (const Map.empty) entries
putStrLn ("Found " ++ pluralize (Map.size packages) "package" ++ ".")
putStrLn ""
let components =
packages & Map.map (Set.map Version.versionBranch) &
Map.map Set.toAscList &
Map.map (map length) &
Map.mapMaybe mode
putStrLn ("Number of version components => number of packages")
components & toMapOfSets & Map.map Set.size & showMap & putStrLn
let majors =
packages & Map.map (Set.map Version.versionBranch) &
Map.map Set.toAscList &
Map.map (Maybe.mapMaybe Maybe.listToMaybe) &
Map.map List.group &
Map.map length
putStrLn ("Number of major versions => number of packages")
majors & toMapOfSets & Map.map Set.size & showMap & putStrLn
let componentMajors =
components &
Map.mapMaybeWithKey
(\k v -> do
x <- Map.lookup k majors
pure (v, x))
putStrLn
("Number of (version components, major versions) => number of packages")
componentMajors & toMapOfSets & Map.map Set.size & showMap & putStrLn
let current =
packages & Map.mapMaybe Set.maxView & Map.map fst &
Map.map Version.versionBranch &
Map.mapMaybe Maybe.listToMaybe
putStrLn ("Current major version => number of packages")
current & toMapOfSets & Map.map Set.size & showMap & putStrLn
let componentCurrent =
components &
Map.mapMaybeWithKey
(\k v -> do
x <- Map.lookup k current
pure (v, x))
putStrLn
("(Number of version components, current major version) => number of packages")
componentCurrent & toMapOfSets & Map.map Set.size & showMap & putStrLn
let releases = packages & Map.map Set.size
putStrLn ("Number of releases => number of packages")
releases & toMapOfSets & Map.map Set.size & showMap & putStrLn
type Package = String
type Versions = Set.Set Version.Version
update :: Tar.Entry -> Map.Map Package Versions -> Map.Map Package Versions
update entry x =
case getMetadata entry of
Nothing -> x
Just (package, version) ->
Map.insertWith Set.union package (Set.singleton version) x
getMetadata :: Tar.Entry -> Maybe (Package, Version.Version)
getMetadata entry = do
Monad.guard (isNormalFile entry)
let path = Tar.entryPath entry
Monad.guard (isCabalFile path)
(package, rawVersion) <-
case FilePath.splitPath path of
[package, version, _] -> Just (safeInit package, safeInit version)
_ -> Nothing
version <- readVersion rawVersion
Just (package, version)
isNormalFile :: Tar.Entry -> Bool
isNormalFile entry =
case Tar.entryContent entry of
Tar.NormalFile _ _ -> True
_ -> False
isCabalFile :: FilePath -> Bool
isCabalFile path =
case FilePath.takeExtension path of
".cabal" -> True
_ -> False
safeInit :: [a] -> [a]
safeInit x =
case x of
[] -> []
_ -> init x
readVersion :: String -> Maybe Version.Version
readVersion x = do
let parses = ReadP.readP_to_S Version.parseVersion x
parse <- safeLast parses
case parse of
(version, "") -> Just version
_ -> Nothing
safeLast :: [a] -> Maybe a
safeLast x =
case x of
[] -> Nothing
_ -> Just (last x)
pluralize :: Int -> String -> String
pluralize count singular =
let plural = singular ++ "s"
word =
case count of
1 -> singular
_ -> plural
in unwords [show count, word]
mode
:: Ord a
=> [a] -> Maybe a
mode xs =
xs & List.sort & List.group & map (\x -> (length x, x)) &
List.sortBy (Ord.comparing Ord.Down) &
map snd &
Maybe.mapMaybe Maybe.listToMaybe &
Maybe.listToMaybe
toMapOfSets
:: (Ord k, Ord v)
=> Map.Map k v -> Map.Map v (Set.Set k)
toMapOfSets x =
x & Map.toAscList & map Tuple.swap & map (\(k, v) -> (k, Set.singleton v)) &
Map.fromListWith Set.union
showMap
:: (Show k, Show v)
=> Map.Map k v -> String
showMap x =
x & Map.toAscList & map (\(k, v) -> show k ++ " => " ++ show v) & unlines
2016-12-21 20:45:29.808761 UTC
Getting package index ...
Got package index.
Found 10644 packages.
Number of version components => number of packages
1 => 21
2 => 1715
3 => 4256
4 => 4639
5 => 9
6 => 2
8 => 2
Number of major versions => number of packages
1 => 9723
2 => 685
3 => 142
4 => 60
5 => 17
6 => 12
7 => 2
8 => 2
9 => 1
Number of (version components, major versions) => number of packages
(1,1) => 15
(1,2) => 4
(1,4) => 2
(2,1) => 1589
(2,2) => 87
(2,3) => 21
(2,4) => 10
(2,5) => 5
(2,6) => 3
(3,1) => 3748
(3,2) => 367
(3,3) => 81
(3,4) => 35
(3,5) => 11
(3,6) => 9
(3,7) => 2
(3,8) => 2
(3,9) => 1
(4,1) => 4358
(4,2) => 227
(4,3) => 40
(4,4) => 13
(4,5) => 1
(5,1) => 9
(6,1) => 2
(8,1) => 2
Current major version => number of packages
0 => 8585
1 => 1412
2 => 270
3 => 130
4 => 55
5 => 39
6 => 10
7 => 16
8 => 6
9 => 15
10 => 2
1000 => 1
2007 => 1
2008 => 8
2009 => 17
2010 => 17
2011 => 10
2012 => 5
2013 => 3
2014 => 8
2015 => 7
2016 => 3
2017 => 1
2999 => 1
3000 => 11
3001 => 2
4000 => 1
7373 => 1
10000 => 1
15320 => 1
15321 => 1
15329 => 1
15778 => 1
17072 => 1
20090215 => 1
(Number of version components, current major version) => number of packages
(1,0) => 9
(1,1) => 6
(1,2) => 2
(1,3) => 1
(1,2013) => 1
(1,2016) => 1
(1,2017) => 1
(2,0) => 1332
(2,1) => 289
(2,2) => 46
(2,3) => 20
(2,4) => 11
(2,5) => 5
(2,6) => 2
(2,7) => 1
(2,9) => 1
(2,10) => 1
(2,1000) => 1
(2,15320) => 1
(2,15321) => 1
(2,15329) => 1
(2,15778) => 1
(2,17072) => 1
(2,20090215) => 1
(3,0) => 3199
(3,1) => 701
(3,2) => 130
(3,3) => 59
(3,4) => 25
(3,5) => 27
(3,6) => 7
(3,7) => 15
(3,8) => 5
(3,9) => 11
(3,2007) => 1
(3,2008) => 8
(3,2009) => 16
(3,2010) => 14
(3,2011) => 6
(3,2012) => 4
(3,2013) => 2
(3,2014) => 7
(3,2015) => 6
(3,2016) => 2
(3,3000) => 8
(3,4000) => 1
(3,7373) => 1
(3,10000) => 1
(4,0) => 4037
(4,1) => 415
(4,2) => 91
(4,3) => 47
(4,4) => 19
(4,5) => 7
(4,6) => 1
(4,8) => 1
(4,9) => 3
(4,10) => 1
(4,2009) => 1
(4,2010) => 3
(4,2011) => 4
(4,2012) => 1
(4,2014) => 1
(4,2015) => 1
(4,2999) => 1
(4,3000) => 3
(4,3001) => 2
(5,0) => 6
(5,1) => 1
(5,2) => 1
(5,3) => 1
(6,0) => 2
(8,3) => 2
Number of releases => number of packages
1 => 2168
2 => 1716
3 => 1359
4 => 945
5 => 688
6 => 532
7 => 448
8 => 341
9 => 295
10 => 238
11 => 202
12 => 199
13 => 145
14 => 111
15 => 118
16 => 99
17 => 94
18 => 79
19 => 87
20 => 64
21 => 62
22 => 53
23 => 39
24 => 42
25 => 32
26 => 33
27 => 31
28 => 21
29 => 18
30 => 21
31 => 24
32 => 20
33 => 22
34 => 13
35 => 13
36 => 11
37 => 14
38 => 14
39 => 13
40 => 10
41 => 10
42 => 5
43 => 6
44 => 6
45 => 8
46 => 40
47 => 8
48 => 5
49 => 4
50 => 9
51 => 8
52 => 7
53 => 7
54 => 7
55 => 3
56 => 1
57 => 3
58 => 2
60 => 4
61 => 4
62 => 2
63 => 3
65 => 2
66 => 4
67 => 4
68 => 1
70 => 2
71 => 2
72 => 2
73 => 1
75 => 1
76 => 1
77 => 3
78 => 1
79 => 1
80 => 2
81 => 3
82 => 2
85 => 2
86 => 1
88 => 2
93 => 1
94 => 1
100 => 3
107 => 1
110 => 1
118 => 1
119 => 2
123 => 2
131 => 1
132 => 1
135 => 1
150 => 1
153 => 1
155 => 1
158 => 1
160 => 1
169 => 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment