├── .circleci
└── config.yml
├── .gitignore
├── NAMES.md
├── README.md
├── graph-core
├── .gitignore
├── LICENSE
├── README.md
├── Setup.hs
├── graph-core.cabal
└── src
│ ├── Data
│ └── Core
│ │ ├── Graph.hs
│ │ └── Graph
│ │ ├── NodeManager.hs
│ │ ├── Persistence.hs
│ │ └── PureCore.hs
│ ├── Test
│ ├── Arbitrary.hs
│ ├── Core.hs
│ ├── NodeManager.hs
│ └── Persistence.hs
│ └── Tests.hs
├── helper.hs
├── preview
├── LICENSE
├── README.md
├── Setup.hs
├── preview.cabal
└── src
│ └── Data
│ └── Preview.hs
├── stack.yaml
├── strict-data
├── .DS_Store
├── LICENSE
├── README.md
├── Setup.hs
├── src
│ └── Data
│ │ ├── Choice.hs
│ │ ├── Fail.hs
│ │ ├── Fail
│ │ └── Types.hs
│ │ ├── Map
│ │ ├── Ordered.hs
│ │ └── Unordered.hs
│ │ ├── Option.hs
│ │ ├── StrictList.hs
│ │ ├── StrictList
│ │ └── Types.hs
│ │ ├── StrictTuple.hs
│ │ ├── StrictVector.hs
│ │ └── StrictVector
│ │ └── Mutable.hs
├── strict-data.cabal
└── test
│ ├── Data
│ └── Map
│ │ └── OrderedSpec.hs
│ ├── Doc.hs
│ ├── Fail.hs
│ ├── Option.hs
│ ├── Spec.hs
│ ├── StrictList.hs
│ ├── StrictVector.hs
│ └── StrictVector
│ └── Mutable.hs
├── text-plus
├── LICENSE
├── README.md
├── Setup.hs
├── src
│ └── Data
│ │ └── Text
│ │ └── Plus.hs
├── test
│ ├── Data
│ │ └── Text
│ │ │ └── PlusSpec.hs
│ └── Spec.hs
└── text-plus.cabal
└── util-plus
├── LICENSE
├── README.md
├── Setup.hs
├── src
├── Control
│ └── Applicative
│ │ └── Plus.hs
├── Data
│ └── List
│ │ └── Plus.hs
├── GHC
│ └── Stack
│ │ └── Plus.hs
└── Safe
│ └── Plus.hs
├── test
├── Data
│ └── List
│ │ └── PlusSpec.hs
├── GHC
│ └── Stack
│ │ └── PlusSpec.hs
├── Safe
│ └── PlusSpec.hs
└── Spec.hs
└── util-plus.cabal
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | working_directory: ~/opensource-mono
5 | docker:
6 | - image: fpco/stack-build:lts-8.20
7 | steps:
8 | - checkout
9 | - restore_cache:
10 | key: stack-cache
11 | - run:
12 | name: stack setup
13 | command: 'stack setup'
14 | - run:
15 | name: stack install dependencies
16 | command: 'stack build --test --only-dependencies'
17 | - save_cache:
18 | key: stack-cache
19 | paths:
20 | - ~/.stack
21 | - ~/opensource-mono/.stack-work
22 | - run:
23 | name: stack build
24 | command: 'stack build --fast --pedantic'
25 | - run:
26 | name: stack test
27 | command: 'stack test --fast'
28 | build-nightly:
29 | working_directory: ~/opensource-mono2
30 | docker:
31 | - image: fpco/stack-build:lts-8.20
32 | steps:
33 | - checkout
34 | - restore_cache:
35 | key: stack-cache-nightly
36 | - run:
37 | name: stack setup
38 | command: 'stack --resolver nightly setup'
39 | - run:
40 | name: stack install dependencies
41 | command: 'stack --resolver nightly build --test --only-dependencies'
42 | - save_cache:
43 | key: stack-cache-nightly
44 | paths:
45 | - ~/.stack
46 | - ~/opensource-mono2/.stack-work
47 | - run:
48 | name: stack build
49 | command: 'stack --resolver nightly build --fast --pedantic'
50 | - run:
51 | name: stack test
52 | command: 'stack --resolver nightly test --fast'
53 |
54 | workflows:
55 | version: 2
56 | build_and_test:
57 | jobs:
58 | - build
59 | - build-nightly
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .stack-work/
2 | .HTF/
3 | .DS_Store
--------------------------------------------------------------------------------
/NAMES.md:
--------------------------------------------------------------------------------
1 | # List of Open Sourced modules
2 |
3 | - Cpm.Util.Alternative --> Control.Applicative.Plus
4 | - Cpm.Util.Choice -> Data.Choice
5 | - Cpm.Util.Fail -> Data.Fail
6 | - Cpm.Util.List -> Data.List.Plus
7 | - Cpm.Util.OSMap -> Data.Map.Ordered
8 | - Cpm.Util.Option -> Data.Option
9 | - Cpm.Util.Preview -> Data.Preview
10 | - Cpm.Util.Safe -> Safe.Plus
11 | - Cpm.Util.SourceLocation -> GHC.Stack.Plus
12 | - Cpm.Util.StrictList -> Data.StrictList
13 | - Cpm.Util.Text -> Data.Text.Plus
14 | - Cpm.Util.Tuple -> Data.StrictTuple
15 | - Cpm.Util.USMap -> Data.Map.Unordered
16 | - Cpm.Util.Vector -> Data.StrictVector
17 | - Cpm.Util.Vector.Mutable -> Data.StrictVector.Mutable
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Factis Research Open Source
2 |
3 | [](https://circleci.com/gh/factisresearch/opensource)
4 |
5 | Welcome to our collection of open source packages. All packages here are extracted from our production code for [Checkpad MED](https://www.checkpad.de/).
6 |
--------------------------------------------------------------------------------
/graph-core/.gitignore:
--------------------------------------------------------------------------------
1 | ## HASKELL
2 | dist
3 | cabal-dev
4 | *.o
5 | *.hi
6 | *.chi
7 | *.chs.h
8 | *~
9 | .virtualenv
10 | .hsenv
11 | .cabal-sandbox/
12 | cabal.sandbox.config
13 | cabal.config
14 | .HTF/
15 | .stack-work/
16 |
17 | ## OS X
18 | .DS_Store
19 | .AppleDouble
20 | .LSOverride
21 |
22 | # Icon must end with two \r
23 | Icon
24 |
25 |
26 | # Thumbnails
27 | ._*
28 |
29 | # Files that might appear on external disk
30 | .Spotlight-V100
31 | .Trashes
32 |
33 | # Directories potentially created on remote AFP share
34 | .AppleDB
35 | .AppleDesktop
36 | Network Trash Folder
37 | Temporary Items
38 |
--------------------------------------------------------------------------------
/graph-core/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 - 2016 factis research GmbH
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/graph-core/README.md:
--------------------------------------------------------------------------------
1 | graph-core
2 | =====
3 |
4 | [](https://travis-ci.org/factisresearch/graph-core.git)
5 | [](http://hackage.haskell.org/package/graph-core)
6 |
7 | ## Intro
8 |
9 | Hackage: [graph-core](http://hackage.haskell.org/package/graph-core)
10 | Stackage: [graph-core](https://www.stackage.org/package/graph-core)
11 |
12 | Fast, memory efficient and persistent graph implementation
13 |
14 |
15 | ## Install
16 |
17 | * Using cabal: `cabal install graph-core`
18 | * Using Stack: `stack install graph-core`
19 | * From Source (cabal): `git clone https://github.com/factisresearch/graph-core.git.git && cd graph-core.git && cabal install`
20 | * From Source (stack): `git clone https://github.com/factisresearch/graph-core.git.git && cd graph-core.git && stack build`
21 |
22 |
23 | ## Misc
24 |
25 | ### Supported GHC Versions
26 |
27 |
28 | ### License
29 |
30 | Released under the MIT license.
31 | (c) 2014 - 2016 factis research GmbH
32 |
--------------------------------------------------------------------------------
/graph-core/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/graph-core/graph-core.cabal:
--------------------------------------------------------------------------------
1 | name: graph-core
2 | version: 0.3.0.0
3 | synopsis: Fast, memory efficient and persistent graph implementation
4 | description: A small package providing a powerful and easy to use Haskell graph implementation.
5 | homepage: https://github.com/factisresearch/opensource#readme
6 | license: MIT
7 | license-file: LICENSE
8 | author: Stefan Wehr , David Leuschner , Niklas Baumstark, Jonathan Dimond, Alexander Thiemann
9 | maintainer: Alexander Thiemann
10 | copyright: (c) 2014 - 2016 factis research GmbH
11 | category: Data
12 | build-type: Simple
13 | cabal-version: >=1.8
14 |
15 | library
16 | exposed-modules: Data.Core.Graph, Data.Core.Graph.NodeManager, Data.Core.Graph.Persistence
17 | other-modules: Data.Core.Graph.PureCore
18 | build-depends: base >=4.6 && <5,
19 | hashable >=1.2,
20 | unordered-containers >=0.2,
21 | containers >=0.5,
22 | safe >=0.3,
23 | deepseq >=1.3,
24 | vector >=0.10,
25 | mtl >=2.1
26 | hs-source-dirs: src
27 | ghc-options: -Wall
28 |
29 | test-suite graph-core-tests
30 | type: exitcode-stdio-1.0
31 | hs-source-dirs: src
32 | main-is: Tests.hs
33 | other-modules: Test.NodeManager, Test.Core, Test.Persistence, Test.Arbitrary
34 | build-depends: base >=4.6 && <5,
35 | hashable >=1.2,
36 | unordered-containers >=0.2,
37 | containers >=0.5,
38 | safe >=0.3,
39 | deepseq >=1.3,
40 | vector >=0.10,
41 | QuickCheck >=2.6,
42 | mtl >=2.1,
43 | HTF >=0.11
44 | ghc-options: -Wall -fno-warn-orphans
45 |
46 | source-repository head
47 | type: git
48 | location: https://github.com/factisresearch/opensource.git
49 |
--------------------------------------------------------------------------------
/graph-core/src/Data/Core/Graph.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE BangPatterns #-}
2 | {-# LANGUAGE CPP #-}
3 | {-# LANGUAGE ScopedTypeVariables #-}
4 | {-# LANGUAGE RankNTypes #-}
5 | {-# LANGUAGE TupleSections #-}
6 | {-# LANGUAGE RecordWildCards #-}
7 | module Data.Core.Graph
8 | ( Graph, Node, NodeSet, Edge(..)
9 | , empty, fromEdges, fromAdj, isConsistent
10 | , nodes, edges, children, parents, hasEdge
11 | , edgeCount
12 | , hull, rhull, hullFold, hullFoldM, rhullFold
13 | , addEdge, addEdges, removeEdge, removeEdges
14 | , addNode, removeNode, solitaireNodes
15 | , edgesAdj
16 | )
17 | where
18 |
19 | import Data.Core.Graph.NodeManager hiding (isConsistent, nodes)
20 | import Data.Core.Graph.PureCore
21 |
--------------------------------------------------------------------------------
/graph-core/src/Data/Core/Graph/NodeManager.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE RecordWildCards #-}
2 | {-# LANGUAGE FlexibleContexts #-}
3 | {-# LANGUAGE RankNTypes #-}
4 | {-# LANGUAGE CPP #-}
5 | module Data.Core.Graph.NodeManager
6 | ( NodeManager, Node, NodeMap, NodeSet
7 | , emptyNode
8 | , initNodeManager, emptyNodeManager, getNodeMap
9 | , getNodeHandle, getExistingNodeHandle, lookupNode, unsafeLookupNode
10 | , removeNodeHandle
11 | , getNewNodesSince, keys, hasKey, nodes, toList
12 | , isConsistent
13 | )
14 | where
15 |
16 | import Control.Monad.State.Strict
17 | import Data.Hashable
18 | import Data.Maybe
19 | import qualified Data.HashMap.Strict as HM
20 | import qualified Data.IntMap.Strict as IM
21 | import qualified Data.IntSet as IS
22 | import qualified Data.List as L
23 |
24 | type Node = Int
25 | type NodeMap v = IM.IntMap v
26 | type NodeSet = IS.IntSet
27 |
28 | emptyNode :: Node
29 | emptyNode = -1
30 |
31 | data NodeManager k
32 | = NodeManager
33 | { nm_nodeToKey :: !(NodeMap k)
34 | , nm_keyToNode :: !(HM.HashMap k Node)
35 | , nm_nextNode :: !Node
36 | } deriving (Show, Eq)
37 |
38 | swap :: forall a b. (a, b) -> (b, a)
39 | swap (x,y) = (y,x)
40 |
41 | isConsistent :: (Ord k) => NodeManager k -> Bool
42 | isConsistent (NodeManager{..}) =
43 | IM.size nm_nodeToKey == HM.size nm_keyToNode
44 | && (IM.null nm_nodeToKey || (nm_nextNode > fst (IM.findMax nm_nodeToKey)
45 | && emptyNode < fst (IM.findMin nm_nodeToKey)))
46 | && L.sort (HM.toList nm_keyToNode) == L.sort (map swap (IM.toList nm_nodeToKey))
47 |
48 | -- map must contain only non-negative keys!
49 | initNodeManager :: (Hashable k, Eq k) => NodeMap k -> NodeManager k
50 | initNodeManager nm =
51 | case IM.minViewWithKey nm of
52 | Just ((n, _), _) | n <= emptyNode -> error $ "Invalid node ID: " ++ show n
53 | _ -> NodeManager nm (invert nm) nextNode
54 | where nextNode
55 | | IM.null nm = 0
56 | | otherwise = 1 + fst (IM.findMax nm)
57 | invert im = HM.fromList . map swap $ IM.toList im
58 |
59 | getNodeMap :: (Hashable k, Eq k) => NodeManager k -> NodeMap k
60 | getNodeMap = nm_nodeToKey
61 |
62 | keys :: NodeManager k -> [k]
63 | keys nm =
64 | HM.keys (nm_keyToNode nm)
65 |
66 | hasKey :: (Eq k, Hashable k) => k -> NodeManager k -> Bool
67 | hasKey k nm =
68 | isJust $ HM.lookup k (nm_keyToNode nm)
69 |
70 | toList :: NodeManager k -> [(k, Node)]
71 | toList nm = HM.toList (nm_keyToNode nm)
72 |
73 | nodes :: NodeManager k -> [Node]
74 | nodes nm = IM.keys (nm_nodeToKey nm)
75 |
76 | getNewNodesSince :: Node -> NodeManager k -> NodeMap k
77 | getNewNodesSince n (NodeManager{..}) = snd $ IM.split n nm_nodeToKey
78 |
79 | emptyNodeManager :: forall k. NodeManager k
80 | emptyNodeManager = NodeManager IM.empty HM.empty 0
81 |
82 | getNodeHandle :: (Hashable k, Eq k, MonadState (NodeManager k) m) => k -> m Node
83 | getNodeHandle k =
84 | do NodeManager{..} <- get
85 | case HM.lookup k nm_keyToNode of
86 | Just i -> return i
87 | Nothing ->
88 | do let i = nm_nextNode
89 | put $! NodeManager { nm_nodeToKey = IM.insert i k nm_nodeToKey
90 | , nm_keyToNode = HM.insert k i nm_keyToNode
91 | , nm_nextNode = i + 1
92 | }
93 | return i
94 |
95 | removeNodeHandle :: (Hashable k, Eq k) => Node -> NodeManager k -> NodeManager k
96 | removeNodeHandle i nm@(NodeManager{..}) =
97 | case IM.lookup i nm_nodeToKey of
98 | Just k ->
99 | nm { nm_nodeToKey = IM.delete i nm_nodeToKey
100 | , nm_keyToNode = HM.delete k nm_keyToNode
101 | }
102 | Nothing -> nm
103 |
104 | getExistingNodeHandle :: (Hashable k, Eq k) => k -> NodeManager k -> Maybe Node
105 | getExistingNodeHandle k (NodeManager{..}) = HM.lookup k nm_keyToNode
106 |
107 | lookupNode :: Node -> NodeManager k -> Maybe k
108 | lookupNode i (NodeManager{..}) = IM.lookup i nm_nodeToKey
109 |
110 | unsafeLookupNode :: Node -> NodeManager k -> k
111 | unsafeLookupNode i nm = fromJust $ lookupNode i nm
112 |
--------------------------------------------------------------------------------
/graph-core/src/Data/Core/Graph/Persistence.hs:
--------------------------------------------------------------------------------
1 | module Data.Core.Graph.Persistence
2 | ( PersistentGraph, persistGraph, loadGraph )
3 | where
4 |
5 | import Data.Core.Graph.PureCore
6 | import Data.Core.Graph.NodeManager
7 |
8 | import Data.Hashable
9 | import qualified Data.IntMap.Strict as IM
10 | import qualified Data.Vector.Unboxed as VU
11 |
12 | data PersistentGraph k
13 | = PersistentGraph
14 | { pg_nodeData :: NodeMap k
15 | , pg_graphData :: [(Node, [Node])]
16 | } deriving (Show, Eq)
17 |
18 | persistGraph :: (Eq k, Hashable k) => NodeManager k -> Graph -> PersistentGraph k
19 | persistGraph nodeManager graph =
20 | PersistentGraph
21 | { pg_nodeData = getNodeMap nodeManager
22 | , pg_graphData = map (\(k, vals) -> (k, VU.toList vals)) (IM.toList $ g_adj graph)
23 | }
24 |
25 | loadGraph :: (Eq k, Hashable k) => PersistentGraph k -> (NodeManager k, Graph)
26 | loadGraph (PersistentGraph nodeData graphData) =
27 | (initNodeManager nodeData, fromAdj graphData)
--------------------------------------------------------------------------------
/graph-core/src/Data/Core/Graph/PureCore.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE BangPatterns #-}
2 | {-# LANGUAGE CPP #-}
3 | {-# LANGUAGE ScopedTypeVariables #-}
4 | {-# LANGUAGE RankNTypes #-}
5 | {-# LANGUAGE TupleSections #-}
6 | {-# LANGUAGE RecordWildCards #-}
7 | module Data.Core.Graph.PureCore where
8 |
9 | import Data.Core.Graph.NodeManager hiding (nodes, isConsistent)
10 |
11 | import Control.Applicative hiding (empty)
12 | import Control.Arrow
13 | import Control.DeepSeq
14 | import Control.Monad
15 | import Control.Monad.Identity
16 | import Control.Monad.ST
17 | import Data.Function (on)
18 | import Data.Hashable
19 | import Data.Maybe
20 | import Data.STRef
21 | import qualified Data.Foldable as F
22 | import qualified Data.HashSet as HS
23 | import qualified Data.IntMap.Strict as IM
24 | import qualified Data.IntSet as IS
25 | import qualified Data.List as L
26 | import qualified Data.Vector.Unboxed as VU
27 |
28 | type AdjList = NodeMap (VU.Vector Node)
29 | data Edge = Edge { src :: !Node, tgt :: !Node } deriving (Show, Eq, Ord)
30 |
31 | instance Hashable Edge where
32 | hashWithSalt s (Edge x y) = s `hashWithSalt` x `hashWithSalt` y
33 |
34 | data Graph
35 | = Graph
36 | { g_adj :: !AdjList
37 | , g_radj :: !AdjList
38 | }
39 |
40 | empty :: Graph
41 | empty = Graph IM.empty IM.empty
42 |
43 | invert :: Edge -> Edge
44 | invert (Edge x y) = Edge y x
45 |
46 | instance Show Graph where
47 | show g = "< " ++ L.intercalate ",\n " (map showNode (nodes g)) ++ " >"
48 | where showNode x = show x
49 | ++ " -> ["
50 | ++ L.intercalate "," (map show (VU.toList (children g x)))
51 | ++ "]"
52 |
53 | instance Eq Graph where
54 | a == b = sameItems (nodes a) (nodes b)
55 | && all (\x -> sameItems (VU.toList (children a x)) (VU.toList (children b x)))
56 | (nodes a)
57 | where sameItems x y = IS.fromList x == IS.fromList y
58 |
59 | instance NFData Graph where
60 | rnf (Graph a b) = rnf a `seq` rnf b
61 |
62 | adjToEdges :: [(Node, [Node])] -> [Edge]
63 | adjToEdges = concatMap (\(x, ys) -> map (Edge x) ys)
64 |
65 | edgesAdj :: AdjList -> [Edge]
66 | edgesAdj adj = adjToEdges . map (second VU.toList) $ IM.toList adj
67 |
68 | isConsistent :: Graph -> Bool
69 | isConsistent (Graph{..}) = L.sort forwardEdges == L.sort (map invert (edgesAdj g_radj))
70 | && HS.size (HS.fromList forwardEdges) == length forwardEdges
71 | where forwardEdges = edgesAdj g_adj
72 |
73 | fromEdges :: [Edge] -> Graph
74 | fromEdges edgeList =
75 | Graph { g_adj = mkAdj edgeList
76 | , g_radj = mkAdj $ map invert edgeList
77 | }
78 | where
79 | mkAdj e = IM.fromList $ map (src . head &&& VU.fromList . map tgt)
80 | . L.groupBy ((==) `on` src)
81 | . L.sortBy (compare `on` src) $ e
82 |
83 | fromAdj :: [(Node, [Node])] -> Graph
84 | fromAdj l =
85 | let g1 = fromEdges (adjToEdges l)
86 | solitaires = map fst $ filter (\(_, xs) -> null xs) l
87 | in L.foldl' (\g n -> g { g_adj = IM.insert n VU.empty (g_adj g) }) g1 solitaires
88 |
89 | nodes :: Graph -> [Node]
90 | nodes g = IM.keys (IM.union (g_adj g) (g_radj g))
91 |
92 | edges :: Graph -> [Edge]
93 | edges = edgesAdj . g_adj
94 |
95 | solitaireNodes :: Graph -> [Node]
96 | solitaireNodes g = IM.keys (IM.filter VU.null (IM.union (g_adj g) (g_radj g)))
97 |
98 | edgeCount :: Graph -> Int
99 | edgeCount = F.foldl' (\old (_,adj) -> old + VU.length adj) 0
100 | . IM.toList . g_adj
101 |
102 | children :: Graph -> Node -> VU.Vector Node
103 | children g x = neighbors g (g_adj g) x
104 |
105 | parents :: Graph -> Node -> VU.Vector Node
106 | parents g x = neighbors g (g_radj g) x
107 |
108 | neighbors :: Graph -> AdjList -> Node -> VU.Vector Node
109 | neighbors (Graph{..}) adj x = IM.findWithDefault VU.empty x adj
110 |
111 | hasEdge :: Node -> Node -> Graph -> Bool
112 | hasEdge x y (Graph{..}) = y `VU.elem` IM.findWithDefault VU.empty x g_adj
113 |
114 | addNode :: Node -> Graph -> Graph
115 | addNode x g =
116 | g { g_adj = IM.insertWith (\_new old -> old) x VU.empty (g_adj g) }
117 |
118 | removeNode :: Node -> Graph -> Graph
119 | removeNode x g =
120 | let rmInAdj adj localF =
121 | foldl (\adjList child ->
122 | IM.adjust (VU.filter (/=x)) child adjList
123 | ) (IM.delete x adj) $ VU.toList (localF g x)
124 |
125 | newAdj = rmInAdj (g_adj g) parents
126 | newRAdj = rmInAdj (g_radj g) children
127 | in g { g_adj = newAdj
128 | , g_radj = newRAdj
129 | }
130 |
131 | addEdge :: Node -> Node -> Graph -> Graph
132 | addEdge x y g@(Graph{..}) =
133 | if hasEdge x y g
134 | then g
135 | else Graph { g_adj = alterDef VU.empty (flip VU.snoc y) x g_adj
136 | , g_radj = alterDef VU.empty (flip VU.snoc x) y g_radj
137 | }
138 | where alterDef def f = IM.alter (Just . f . fromMaybe def)
139 |
140 | addEdges :: [Edge] -> Graph -> Graph
141 | addEdges edgeList g = L.foldl' (flip (\(Edge x y) -> addEdge x y)) g edgeList
142 |
143 | removeEdge :: Node -> Node -> Graph -> Graph
144 | removeEdge x y (Graph{..}) =
145 | Graph { g_adj = IM.adjust (VU.filter (/=y)) x g_adj
146 | , g_radj = IM.adjust (VU.filter (/=x)) y g_radj
147 | }
148 |
149 | removeEdges :: [Edge] -> Graph -> Graph
150 | removeEdges edgeList g = L.foldl' (flip (\(Edge x y) -> removeEdge x y)) g edgeList
151 |
152 | hull :: Graph -> Node -> NodeSet
153 | hull g = hullImpl g (g_adj g)
154 |
155 | rhull :: Graph -> Node -> NodeSet
156 | rhull g = hullImpl g (g_radj g)
157 |
158 | hullImpl :: Graph -> AdjList -> Node -> NodeSet
159 | hullImpl (Graph{..}) adj root =
160 | runST $
161 | do vis <- newSTRef IS.empty
162 | let go x =
163 | (IS.member x <$> readSTRef vis) >>=
164 | (flip unless $
165 | do modifySTRef' vis (IS.insert x)
166 | VU.forM_ (IM.findWithDefault VU.empty x adj) go)
167 | go root
168 | readSTRef vis
169 |
170 | rhullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b
171 | rhullFold g f initial node =
172 | runIdentity $ hullFoldImpl (g_radj g) (\x y -> return (f x y)) initial node
173 |
174 | -- FIXME: benchmark against old hullFold implementation
175 | hullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b
176 | hullFold g f initial node =
177 | runIdentity $ hullFoldImpl (g_adj g) (\x y -> return (f x y)) initial node
178 |
179 | hullFoldM :: Monad m => Graph -> (b -> Node -> m b) -> b -> Node -> m b
180 | hullFoldM g = hullFoldImpl (g_adj g)
181 |
182 | hullFoldImpl :: Monad m => AdjList -> (b -> Node -> m b) -> b -> Node -> m b
183 | hullFoldImpl adj f initial root =
184 | go IS.empty initial [root]
185 | where
186 | go _ acc [] = return acc
187 | go !visited !acc (x:xs) =
188 | if (IS.member x visited)
189 | then go visited acc xs
190 | else do newAcc <- f acc x
191 | let succs = IM.findWithDefault VU.empty x adj
192 | go (IS.insert x visited) newAcc (xs ++ VU.toList succs)
193 |
--------------------------------------------------------------------------------
/graph-core/src/Test/Arbitrary.hs:
--------------------------------------------------------------------------------
1 | module Test.Arbitrary where
2 |
3 | import Test.QuickCheck
4 | import Control.Monad (forM)
5 | import Data.Core.Graph.NodeManager (NodeMap)
6 | import Data.Core.Graph.PureCore (Graph, empty, fromAdj)
7 | import qualified Data.IntMap.Strict as IM
8 |
9 |
10 | newtype TestNodeMap v = TestNodeMap(NodeMap v) deriving Show
11 |
12 | instance Arbitrary v => Arbitrary (TestNodeMap v) where
13 | arbitrary = fmap (TestNodeMap . IM.fromList . map (\(NonNegative i, x) -> (i, x))) arbitrary
14 |
15 | instance Arbitrary Graph where
16 | arbitrary = frequency [(1, return empty), (20, denseGraph)]
17 | where denseGraph =
18 | do n <- choose (0, 30::Int)
19 | let nodeList = [1..n]
20 | adj <- forM nodeList $ \i ->
21 | do bits <- vectorOf n arbitrary
22 | return (i, [ x | (x,b) <- zip nodeList bits, b ])
23 | return $ fromAdj adj
24 |
--------------------------------------------------------------------------------
/graph-core/src/Test/Core.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Test.Core where
3 |
4 | import Data.Core.Graph.PureCore
5 | import Data.Core.Graph.NodeManager (Node)
6 |
7 | import Control.Monad
8 | import Test.Arbitrary ()
9 | import Test.Framework
10 | import qualified Data.IntSet as IS
11 |
12 | e :: (Node, Node) -> Edge
13 | e (x,y) = Edge x y
14 |
15 | testGraphA :: Graph
16 | testGraphA = fromAdj [(1, [2,3]), (2, [3,4]), (3, [4])]
17 |
18 | testGraphB :: Graph
19 | testGraphB = fromEdges [e (1,2), e (2,1)]
20 |
21 | test_hasEdge :: IO ()
22 | test_hasEdge =
23 | do assertEqual True $ hasEdge 2 4 testGraphA
24 | forM_ (edges testGraphA) $ \(Edge a b) ->
25 | assertEqual True (hasEdge a b testGraphA)
26 | assertEqual False $ hasEdge 1 4 testGraphA
27 | assertEqual False $ hasEdge 1000 200 testGraphA
28 |
29 | test_removeEdges :: IO ()
30 | test_removeEdges =
31 | do assertEqual (fromAdj [(1, [2]), (2, [3,4]), (3, [4]), (4, [])])
32 | (removeEdges [e (1,3), e (4,1)] testGraphA)
33 |
34 | test_removeNode :: IO ()
35 | test_removeNode =
36 | let orig = fromAdj [(1, [2]), (2, [3,4]), (3, [4]), (4, [])]
37 | res1 = fromAdj [(1, [2]), (2, [3]), (3, [])]
38 | res2 = fromAdj [(1, [2]), (2, [4]), (4, [])]
39 | res3 = fromAdj [(1, []), (3, [4]), (4, [])]
40 |
41 | in do assertEqual res1 (removeNode 4 orig)
42 | assertEqual res2 (removeNode 3 orig)
43 | assertEqual res3 (removeNode 2 orig)
44 | assertEqual orig (removeNode 5 orig)
45 |
46 | test_hull :: IO ()
47 | test_hull =
48 | do assertEqual (IS.fromList [4]) (hull testGraphA 4)
49 | assertEqual (IS.fromList [1,2,3,4]) (hull testGraphA 1)
50 | assertEqual (IS.fromList [2,3,4]) (hull testGraphA 2)
51 | assertEqual (IS.fromList [3,4]) (hull testGraphA 3)
52 | assertEqual (IS.fromList [100]) (hull testGraphA 100)
53 | assertEqual (IS.fromList [1,2]) (hull testGraphB 1)
54 |
55 | test_rhull :: IO ()
56 | test_rhull =
57 | do assertEqual (IS.fromList [1,2,3]) (rhull testGraphA 3)
58 | assertEqual (IS.fromList [1]) (rhull testGraphA 1)
59 | assertEqual (IS.fromList [1,2]) (rhull testGraphB 1)
60 |
61 | test_hullFold :: IO ()
62 | test_hullFold =
63 | do assertEqual 10 (hullFold testGraphA (+) 0 1)
64 | assertEqual 4 (hullFold testGraphA (+) 0 4)
65 | assertEqual 100 (hullFold testGraphA (+) 0 100)
66 | assertEqual 3 (hullFold testGraphB (+) 0 1)
67 |
68 | prop_fromEdgesAddEdges :: Graph -> Bool
69 | prop_fromEdgesAddEdges g =
70 | let isCons = isConsistent new
71 | isEq = new == g
72 | in if isCons && isEq
73 | then True
74 | else error ("g=" ++ show g ++ ", new=" ++ show new ++
75 | ", isCons=" ++ show isCons ++ ", isEq=" ++ show isEq)
76 | where
77 | new =
78 | foldl (\g' n -> addNode n g') (addEdges (edges g) empty) (solitaireNodes g)
79 |
80 | prop_fromEdgesToEdges :: Graph -> Bool
81 | prop_fromEdgesToEdges g = isConsistent new && new == g
82 | where
83 | new =
84 | foldl (\g' n -> addNode n g') (fromEdges (edges g)) (solitaireNodes g)
85 |
--------------------------------------------------------------------------------
/graph-core/src/Test/NodeManager.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Test.NodeManager where
3 |
4 | import Data.Core.Graph.NodeManager
5 |
6 | import Test.Arbitrary
7 | import Test.Framework
8 | import Control.Monad.State.Strict
9 | import qualified Data.IntMap.Strict as IM
10 | import qualified Data.List as L
11 |
12 | assertConsistent :: StateT (NodeManager Char) IO ()
13 | assertConsistent = get >>= liftIO . assertEqual True . isConsistent
14 |
15 | prop_init :: TestNodeMap String -> Property
16 | prop_init (TestNodeMap m) = uniqueValues m && all (>=0) (IM.keys m)
17 | ==> isConsistent new && m == getNodeMap new
18 | where new = initNodeManager m
19 | uniqueValues im = IM.size im == length (L.nub $ IM.elems im)
20 |
21 | test_getNewNodesSince :: IO ()
22 | test_getNewNodesSince =
23 | flip evalStateT emptyNodeManager $
24 | do _ <- getNodeHandle 'a'
25 | n2 <- getNodeHandle 'b'
26 | n3 <- getNodeHandle 'c'
27 | n4 <- getNodeHandle 'd'
28 | new <- gets (getNewNodesSince n2)
29 | liftIO $ assertEqual (IM.fromList [(n3, 'c'), (n4, 'd')]) new
30 |
31 | test_getNodeHandle :: IO ()
32 | test_getNodeHandle =
33 | flip evalStateT emptyNodeManager $
34 | do n1 <- getNodeHandle 'a'
35 | n2 <- getNodeHandle 'a'
36 | liftIO $ assertEqual n1 n2
37 | n3 <- getNodeHandle 'b'
38 | n4 <- getNodeHandle 'b'
39 | liftIO $ assertEqual n3 n4
40 | n5 <- getNodeHandle 'a'
41 | liftIO $ assertEqual n1 n5
42 | liftIO $ assertNotEqual n1 n3
43 | assertConsistent
44 |
45 | test_lookupNode :: IO ()
46 | test_lookupNode =
47 | flip evalStateT emptyNodeManager $
48 | do n1 <- getNodeHandle 'a'
49 | n2 <- getNodeHandle 'b'
50 | x1 <- gets $ lookupNode n1
51 | x2 <- gets $ lookupNode n2
52 | x3 <- gets $ lookupNode 123
53 | liftIO $ assertEqual (Just 'a') x1
54 | liftIO $ assertEqual (Just 'b') x2
55 | liftIO $ assertEqual Nothing x3
56 | assertConsistent
57 |
--------------------------------------------------------------------------------
/graph-core/src/Test/Persistence.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Test.Persistence where
3 |
4 | import Data.Core.Graph
5 | import Data.Core.Graph.NodeManager
6 | import Data.Core.Graph.Persistence
7 |
8 | import Test.Arbitrary
9 | import Test.Framework
10 |
11 | prop_persistence :: TestNodeMap Char -> Graph -> Bool
12 | prop_persistence (TestNodeMap nodeMap) graph =
13 | let nodeMgr = initNodeManager nodeMap
14 | (nodeMgr', graph') = loadGraph (persistGraph nodeMgr graph)
15 | in (nodeMgr' == nodeMgr && graph == graph')
16 |
--------------------------------------------------------------------------------
/graph-core/src/Tests.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Main where
3 |
4 | import Test.Framework
5 | import {-@ HTF_TESTS @-} Test.Core
6 | import {-@ HTF_TESTS @-} Test.NodeManager
7 | import {-@ HTF_TESTS @-} Test.Persistence
8 |
9 | main :: IO ()
10 | main = htfMain htf_importedTests
11 |
--------------------------------------------------------------------------------
/helper.hs:
--------------------------------------------------------------------------------
1 | -- Run this using
2 | -- @stack exec -- runhaskell helper.hs@
3 | {-# LANGUAGE OverloadedStrings #-}
4 |
5 | import System.Directory
6 | import System.Environment
7 | import System.FilePath
8 | import qualified Data.Text as T
9 | import qualified Data.Text.IO as T
10 |
11 | main :: IO ()
12 | main =
13 | do args <- getArgs
14 | case args of
15 | ["opensource", file, package, newName] ->
16 | do ct <- T.readFile file
17 | let fileName =
18 | package ++ "/src/"
19 | ++ T.unpack (T.replace "." "/" (T.pack newName))
20 | ++ ".hs"
21 | baseDir = takeDirectory fileName
22 | createDirectoryIfMissing True baseDir
23 | T.writeFile fileName ct
24 | _ ->
25 | putStrLn "Usage: helper.hs opensource [src] [pkg] [tgt]"
26 |
--------------------------------------------------------------------------------
/preview/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright factis research GmbH (c) 2017
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Kierán Meinhardt nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/preview/README.md:
--------------------------------------------------------------------------------
1 | # preview
2 |
--------------------------------------------------------------------------------
/preview/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/preview/preview.cabal:
--------------------------------------------------------------------------------
1 | name: preview
2 | version: 0.1.0.4
3 | synopsis: The method of previewing data (instead of wholly show-ing it)
4 | description: The method of previewing data (instead of wholly show-ing it)
5 | homepage: https://github.com/factisresearch/opensource#readme
6 | license: BSD3
7 | license-file: LICENSE
8 | author: factis research GmbH
9 | maintainer: kieran.meinhardt@gmail.com
10 | copyright: 2017 factis research GmbH
11 | category: Data
12 | build-type: Simple
13 | extra-source-files: README.md
14 | cabal-version: >=1.10
15 |
16 | library
17 | hs-source-dirs: src
18 | exposed-modules: Data.Preview
19 | build-depends: base >= 4.7 && < 5
20 | , containers
21 | , pretty
22 | , strict-data
23 | , text
24 | , util-plus
25 | default-language: Haskell2010
26 |
27 | source-repository head
28 | type: git
29 | location: https://github.com/factisresearch/opensource.git
30 |
--------------------------------------------------------------------------------
/preview/src/Data/Preview.hs:
--------------------------------------------------------------------------------
1 |
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE PatternSynonyms #-}
4 | module Data.Preview
5 | (Preview(..), preview, previewNamedSet, previewNamedList, previewList
6 | ,previews, previewRec ,previewRec', previewKv, showKv
7 | ,previewElems, previewsElems, pprMapping, previewList', previewsPrecMapping
8 | ,Ppr(..), Ppr'(..), Doc, pretty, prettyText, docFromStr, shortPreviewStr
9 | ,docFromText,angles
10 | )
11 | where
12 |
13 | import Data.Bifunctor
14 | import Data.Choice (Choice(..), mergeChoice)
15 | import Data.Fail (Fail(..))
16 | import Data.List.Plus
17 | import Data.Option (Option(..))
18 | import Data.StrictList (StrictList, toLazyList)
19 | import Data.StrictTuple (Pair(..))
20 | import qualified Data.Map.Ordered as OM
21 | import qualified Data.Map.Unordered as UM
22 |
23 | import Data.Int (Int32, Int64)
24 | import Data.Map (Map)
25 | import Data.Maybe (fromMaybe)
26 | import Data.Set (Set)
27 | import Data.Word (Word8, Word64)
28 | import Text.PrettyPrint.HughesPJ (Doc, (<>), (<+>))
29 | import qualified Data.Foldable as F
30 | import qualified Data.Map as Map
31 | import qualified Data.Set as Set
32 | import qualified Data.Text as T
33 | import qualified Text.PrettyPrint.HughesPJ as P
34 |
35 | _LIST_PREVIEW_ELEMS_ :: Int
36 | _LIST_PREVIEW_ELEMS_ = 10
37 |
38 | -- | Conversion of values to short readable strings.
39 | -- Preview allows defining short and readable representations of potentially huge data structures
40 | -- that can be used in logs for example. E.g. the Preview instance for lists may only print the
41 | -- values at the beginning of the list and omit the rest.
42 | class Preview a where
43 | -- | Create a preview String for the given value.
44 | --
45 | -- 'previewsPrec' should satisfy the law
46 | --
47 | -- > previewsPrec d x r ++ s == previewsPrec d x (r ++ s)
48 | previewsPrec
49 | :: Int
50 | -- ^ The operator precedence of the enclosing context (a number from 0 to 11). Function
51 | -- application has precedence 10.
52 | -> a -- ^ the value to be previewed
53 | -> String -- ^ the string to be appended at the end of the output (for constant time append)
54 | -> String
55 |
56 | instance Preview () where previewsPrec = showsPrec
57 | instance Preview Char where previewsPrec = showsPrec
58 | instance Preview Int where previewsPrec = showsPrec
59 | instance Preview Bool where previewsPrec = showsPrec
60 | instance Preview a => Preview [a] where previewsPrec = previewList
61 | instance Preview T.Text where previewsPrec = previewsText
62 |
63 | instance Ppr T.Text where
64 | ppr = P.text . T.unpack
65 |
66 | instance (Preview a, Preview b) => Preview (Either a b) where
67 | previewsPrec prec eAB s =
68 | case eAB of
69 | Left a -> previewsPrec prec a s
70 | Right b -> previewsPrec prec b s
71 |
72 | instance (Preview a) => Preview (Maybe a) where
73 | previewsPrec prec mA s =
74 | case mA of
75 | Nothing -> showString "Nothing" s
76 | Just a -> previewsPrec prec a s
77 |
78 | instance (Preview a, Preview b) => Preview (a, b) where
79 | previewsPrec prec (a, b) =
80 | showParen (prec >= 10) $
81 | previewsPrec 5 a .
82 | showString ", " .
83 | previewsPrec 5 b
84 |
85 | instance (Preview a, Preview b, Preview c) => Preview (a, b, c) where
86 | previewsPrec prec (a, b, c) =
87 | showString "(" .
88 | previewsPrec prec a .
89 | showString ", " .
90 | previewsPrec prec b .
91 | showString ", " .
92 | previewsPrec prec c .
93 | showString ")"
94 |
95 | instance Preview Word8 where
96 | previewsPrec = showsPrec
97 |
98 | instance Preview Word64 where
99 | previewsPrec = showsPrec
100 |
101 | instance Preview Int32 where
102 | previewsPrec = showsPrec
103 |
104 | instance Preview Int64 where
105 | previewsPrec = showsPrec
106 |
107 | instance (Preview k, Preview v) => Preview (Map k v) where
108 | previewsPrec p = previewsPrecMapping p . Map.toList
109 |
110 | instance Preview a => Preview (Fail a) where
111 | previewsPrec p x =
112 | case x of
113 | Err msg -> showParen (p > 10) $ showString "Fail " . showsPrec 5 msg
114 | Ok a -> previewsPrec p a
115 |
116 | instance (Preview a, Preview b) => Preview (Pair a b) where
117 | previewsPrec p (x :!: y) = previewsPrec p (x, y)
118 |
119 | instance (Ppr a, Ppr b) => Ppr (Pair a b) where
120 | ppr (a :!: b) = P.parens ((ppr a <> P.semi) P.<+> ppr b)
121 |
122 | instance Preview a => Preview (Option a) where
123 | previewsPrec prec mA s =
124 | case mA of
125 | None -> showsPrec prec ("None"::String) s
126 | Some a -> previewsPrec prec a s
127 |
128 | instance Preview a => Preview (StrictList a) where
129 | previewsPrec x y = previewList x (toLazyList y)
130 |
131 | instance Ppr a => Ppr (StrictList a) where
132 | ppr = pprMany
133 |
134 | instance Ppr a => Ppr (Option a) where
135 | ppr None = P.text "None"
136 | ppr (Some a) = ppr a
137 |
138 | instance (Preview a, Preview b) => Preview (Choice a b) where
139 | previewsPrec p choice =
140 | case choice of
141 | This a -> previewsPrec p a
142 | That b -> previewsPrec p b
143 |
144 | instance (Preview k, Preview v) => Preview (OM.OSMap k v) where
145 | previewsPrec p x = previewsPrec p (OM.toDataMap x)
146 |
147 | previewsPrecMapping :: (Preview k, Preview v) => t -> [(k, v)] -> String -> String
148 | previewsPrecMapping _ =
149 | (showString "{ " .) .
150 | foldr (.) (showString "}") .
151 | intersperse (showString "\n, ") .
152 | map (\(k,xs) -> previewsPrec 10 k . showString " -> " . previewsPrec 10 xs) .
153 | take _LIST_PREVIEW_ELEMS_
154 |
155 | class Ppr a where
156 | ppr :: a -> Doc
157 | pprMany :: Foldable f => f a -> Doc
158 | pprMany xs = P.brackets (P.sep $ P.punctuate P.comma $ fmap ppr (F.toList xs))
159 |
160 | class Ppr' k where
161 | ppr' :: Ppr a => k a -> Doc
162 |
163 | instance Ppr () where
164 | ppr () = P.text "()"
165 |
166 | instance Ppr Int64 where
167 | ppr i = P.text (show i)
168 |
169 | instance Ppr Char where
170 | ppr = P.char
171 | pprMany xs = P.char '"' <> F.foldl' (\d x -> d <> ppr x) P.empty xs <> P.char '"'
172 |
173 | instance Ppr Bool where
174 | ppr x = if x then P.text "True" else P.text "False"
175 |
176 | instance Ppr a => Ppr [a] where
177 | ppr = pprMany
178 |
179 | instance Ppr a => Ppr (Set a) where
180 | ppr = pprMany
181 |
182 | instance (Ppr a, Ppr b) => Ppr (Map a b) where
183 | ppr = pprMapping . Map.toList
184 |
185 | instance Ppr Doc where
186 | ppr = id
187 |
188 | instance Ppr a => Ppr (Fail a) where
189 | ppr (Ok x) = "Ok" <+> ppr x
190 | ppr (Err msg) = "Fail" <+> docFromStr (show msg)
191 |
192 | pprMapping :: (Ppr a, Ppr b) => [(a, b)] -> Doc
193 | pprMapping xs =
194 | P.braces (P.sep $ P.punctuate P.comma $ fmap pprTuple xs)
195 | where
196 | pprTuple (a, b) = P.sep [ppr a <+> P.text "->", P.nest 4 $ ppr b]
197 |
198 | instance (Ppr a, Ppr b) => Ppr (a, b) where
199 | ppr (a, b) = P.parens (ppr a <> P.comma <+> ppr b)
200 |
201 | instance Ppr Int where
202 | ppr = docFromStr . show
203 |
204 | instance Ppr Integer where
205 | ppr = docFromStr . show
206 |
207 | instance Ppr Int32 where
208 | ppr = docFromStr . show
209 |
210 | instance Ppr Word8 where
211 | ppr = docFromStr . show
212 |
213 | instance Ppr Double where
214 | ppr = docFromStr . show
215 |
216 | instance Ppr a => Ppr (Maybe a) where
217 | ppr (Just x) = ppr x
218 | ppr Nothing = P.text "Nothing"
219 |
220 | instance Ppr Word64 where
221 | ppr = docFromStr . show
222 |
223 | instance (Ppr k, Ppr v) => Ppr (OM.OSMap k v) where
224 | ppr = pprMapping . OM.toList
225 |
226 | instance (Ppr k, Ppr v) => Ppr (UM.USMap k v) where
227 | ppr = pprMapping . UM.toList
228 |
229 | instance (Ppr a, Ppr b) => Ppr (Choice a b) where
230 | ppr = mergeChoice . bimap ppr ppr
231 |
232 | pretty :: Ppr a => a -> String
233 | pretty = P.renderStyle (P.style { P.mode = P.LeftMode }) . ppr
234 |
235 | prettyText :: Ppr a => a -> T.Text
236 | prettyText = T.pack . pretty
237 |
238 | docFromStr :: String -> Doc
239 | docFromStr = P.text
240 |
241 | docFromText :: T.Text -> Doc
242 | docFromText = P.text . T.unpack
243 |
244 | preview :: Preview a => a -> String
245 | preview x = previewsPrec 5 x ""
246 |
247 | previews :: Preview a => a -> String -> String
248 | previews = previewsPrec 0
249 |
250 | previewRec :: Int -> String -> [(String, Int -> String -> String)] -> String -> String
251 | previewRec prec tyName fields =
252 | previewRec' prec tyName (map mapField fields)
253 | where
254 | mapField (n,f) = showString n . showString "=" . f 11
255 |
256 | previewRec' :: Int -> String -> [String -> String] -> String -> String
257 | previewRec' prec tyName fields =
258 | showParen (prec > 10) $
259 | showString tyName .
260 | showString " { " .
261 | foldl' (.) id (intersperse (showString ", ") fields) .
262 | showString " }"
263 |
264 | previewKv :: Preview a => String -> a -> String -> String
265 | previewKv name x =
266 | showString name . showString "=" . (previewsPrec 5 x)
267 |
268 | showKv :: Show a => String -> a -> String -> String
269 | showKv name x =
270 | showString name . showString "=" . (showsPrec 5 x)
271 |
272 | previewNamedSet :: String -> t -> Set a -> String -> String
273 | previewNamedSet name _prec set =
274 | showString "|" . showString name . showString "|=" . showString (show (Set.size set))
275 |
276 | previewNamedList :: String -> t -> [a] -> String -> String
277 | previewNamedList name _prec xs =
278 | showString "|" . showString name . showString "|=" . showString (show (length xs))
279 |
280 | previewList' :: Preview a => Maybe Int -> Int -> [a] -> String -> String
281 | previewList' maxElems _prec xs =
282 | case xs of
283 | [] -> showString "[]"
284 | (x:rest)
285 | | maxElems == Nothing || length xs <= fromMaybe 0 maxElems ->
286 | showString "[" .
287 | foldl' (.) id (intersperse (showString ", ") (map (previewsPrec 11) xs)) .
288 | showString "]"
289 | | otherwise ->
290 | showString "[" .
291 | previewsPrec 5 x .
292 | if null rest
293 | then showString "]"
294 | else showString "... " .
295 | showsPrec 5 (length rest) .
296 | showString " more elems)]"
297 |
298 | previewList :: Preview a => Int -> [a] -> String -> String
299 | previewList = previewList' (Just _LIST_PREVIEW_ELEMS_)
300 |
301 | previewsElems :: (Foldable f, Preview a) => Int -> f a -> String -> String
302 | previewsElems _prec xs =
303 | showString "[" .
304 | foldl' (.) id (intersperse (showString ", ") (map (previewsPrec 5) (F.toList xs))) .
305 | showString "]"
306 |
307 | previewElems :: (Foldable t, Preview a) => t a -> String
308 | previewElems xs = previewsElems 5 xs ""
309 |
310 | previewsText :: Int -> T.Text -> ShowS
311 | previewsText _ t
312 | | T.length t < 65 = showString (T.unpack t)
313 | | otherwise = showString (T.unpack (T.take 65 t)) . showString "..."
314 |
315 | shortPreviewStr :: Int -> String -> String
316 | shortPreviewStr n s =
317 | if length s <= n
318 | then s
319 | else take n s ++ "..."
320 |
321 | angles :: Doc -> Doc
322 | angles p = P.char '<' <> p <> P.char '>'
323 |
--------------------------------------------------------------------------------
/stack.yaml:
--------------------------------------------------------------------------------
1 | resolver: lts-9.0
2 | packages:
3 | - preview
4 | - strict-data
5 | - text-plus
6 | - util-plus
7 |
--------------------------------------------------------------------------------
/strict-data/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/factisresearch/opensource/d8b1fdf39d945bfcbc045ca9f73ab5aa84f89fa0/strict-data/.DS_Store
--------------------------------------------------------------------------------
/strict-data/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Alexander Thiemann (c) 2016
2 | Copyright factis research GmbH (c) 2017
3 |
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above
13 | copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided
15 | with the distribution.
16 |
17 | * Neither the name of Alexander Thiemann nor the names of other
18 | contributors may be used to endorse or promote products derived
19 | from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/strict-data/README.md:
--------------------------------------------------------------------------------
1 | # Various strict data structures
2 |
3 | [](https://circleci.com/gh/factisresearch/opensource-mono)
4 |
5 | This package currently contains strict data structures and useful instances for:
6 |
7 | * `Data.Choice` to replace `Data.Either`
8 | * `Data.Fail` for a sane error monad
9 | * `Data.Option` to replace `Data.Maybe`
10 | * `Data.StrictList` to replace `Data.List`
11 | * `Data.StrictTuple` to replace tuples
12 | * `Data.StrictVector` and `Data.StrictVector.Mutable` to replace `Data.Vector`
13 | and `Data.Vector.Mutable`
14 |
--------------------------------------------------------------------------------
/strict-data/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/strict-data/src/Data/Choice.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveDataTypeable #-}
2 | module Data.Choice where
3 |
4 | import Control.DeepSeq (NFData(..))
5 | import Data.Bifunctor
6 | import Data.Data
7 | import Data.Hashable
8 | import Safe.Plus
9 | import Test.QuickCheck
10 |
11 | -- | 'Choice' is a version of 'Either' that is strict on both the 'Left' side (called 'This')
12 | -- and the 'Right' side (called 'That').
13 | --
14 | -- Note: 'Choice' is not used as an error monad. Use 'Data.Fail.Fail' for that.
15 | data Choice a b
16 | = This !a
17 | | That !b
18 | deriving (Eq, Ord, Read, Show, Typeable, Data)
19 |
20 | -- | 'Choice''s version of 'either'
21 | choice :: (a -> c) -> (b -> c) -> Choice a b -> c
22 | choice fa fb = mergeChoice . bimap fa fb
23 |
24 | -- |
25 | -- >>> this (This "foo") :: Maybe String
26 | -- Just "foo"
27 | --
28 | -- >>> this (That "bar") :: Maybe String
29 | -- Nothing
30 | this :: Monad m => Choice a b -> m a
31 | this (This a) = return a
32 | this _ = safeFail "This is a that"
33 |
34 | -- |
35 | -- >>> that (This "foo") :: Maybe String
36 | -- Nothing
37 | --
38 | -- >>> that (That "bar") :: Maybe String
39 | -- Just "bar"
40 | that :: Monad m => Choice a b -> m b
41 | that (That a) = return a
42 | that _ = safeFail "That is a this"
43 |
44 | -- |
45 | -- >>> these [This "foo", This "bar", That "baz", This "quux"]
46 | -- ["foo","bar","quux"]
47 | these :: [Choice a b] -> [a]
48 | these = concatMap this
49 |
50 | -- |
51 | -- >>> those [This "foo", This "bar", That "baz", This "quux"]
52 | -- ["baz"]
53 | those :: [Choice a b] -> [b]
54 | those = concatMap that
55 |
56 | -- |
57 | -- >>> eitherToChoice (Left 1)
58 | -- This 1
59 | --
60 | -- >>> eitherToChoice (Right 5)
61 | -- That 5
62 | eitherToChoice :: Either a b -> Choice a b
63 | eitherToChoice = either This That
64 |
65 | -- |
66 | -- >>> mergeChoice (This 5 :: Choice Int Int)
67 | -- 5
68 | --
69 | -- >>> mergeChoice (That 'c' :: Choice Char Char)
70 | -- 'c'
71 | mergeChoice :: Choice a a -> a
72 | mergeChoice x =
73 | case x of
74 | This y -> y
75 | That y -> y
76 |
77 | instance Bifunctor Choice where
78 | bimap f g x =
79 | case x of
80 | This a -> This (f a)
81 | That b -> That (g b)
82 |
83 | instance (Hashable a, Hashable b) => Hashable (Choice a b) where
84 | hashWithSalt s (This x) = s `hashWithSalt` (0 :: Int) `hashWithSalt` x
85 | hashWithSalt s (That x) = s `hashWithSalt` (1 :: Int) `hashWithSalt` x
86 |
87 | instance Applicative (Choice e) where
88 | pure = That
89 | This e <*> _ = This e
90 | That f <*> r = fmap f r
91 |
92 | instance Functor (Choice a) where
93 | fmap = second
94 |
95 | instance Monad (Choice e) where
96 | return = That
97 | This l >>= _ = This l
98 | That r >>= k = k r
99 |
100 | instance (Arbitrary a, Arbitrary b) => Arbitrary (Choice a b) where
101 | arbitrary =
102 | do bool <- arbitrary
103 | if bool
104 | then fmap This arbitrary
105 | else fmap That arbitrary
106 |
107 | shrink (This a) = map This $ shrink a
108 | shrink (That b) = map That $ shrink b
109 |
110 | instance (NFData a, NFData b) => NFData (Choice a b) where
111 | rnf (This x) = rnf x
112 | rnf (That y) = rnf y
113 |
--------------------------------------------------------------------------------
/strict-data/src/Data/Fail.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2 | {-# OPTIONS_GHC -fno-warn-orphans #-}
3 | {-# LANGUAGE FlexibleInstances #-}
4 | {-# LANGUAGE MultiParamTypeClasses #-}
5 | {-# LANGUAGE PatternSynonyms #-}
6 | {-# LANGUAGE Rank2Types #-}
7 | {-# LANGUAGE ScopedTypeVariables #-}
8 | {-# LANGUAGE TypeFamilies #-}
9 | {-# LANGUAGE TypeSynonymInstances #-}
10 | {-# LANGUAGE UndecidableInstances #-}
11 | module Data.Fail
12 | ( Fail(..), pattern Fail, isFail, isOk
13 | , FailT(FailT), runFailT, FIO
14 | , failEitherStr, failEitherShow, failEitherText
15 | , runExceptTFail, failInM, failInM', failInM''
16 | , failToEither, failMaybe, failToMaybe, mapFail
17 | , failSwitch, fromFail
18 | , MonadFailure(..)
19 | , failForIOException, catFails
20 | , eitherToError, errorToEither, liftError, errorToDefault, errorToMaybe, maybeToError, runError
21 | , runExceptTorFail, maybeToFail, eitherToFail
22 | , fromFailString, partitionFails
23 | , safeFromOk
24 | , Control.Monad.Fail.MonadFail
25 | ) where
26 |
27 |
28 | import Data.Fail.Types
29 |
30 | import Control.Applicative (Alternative(..))
31 | import Control.Exception (ErrorCall(..), IOException, catch)
32 | import Control.Monad (MonadPlus(..))
33 | import Control.Monad.Base (MonadBase (..), liftBaseDefault)
34 | import Control.Monad.Catch (MonadThrow (..))
35 | import Control.Monad.Except (ExceptT, runExceptT, MonadError(..))
36 | import Control.Monad.Fail (MonadFail)
37 | import Control.Monad.Fix
38 | import Control.Monad.IO.Class
39 | import Control.Monad.Identity (runIdentity)
40 | import Control.Monad.Reader (ReaderT(..))
41 | import Control.Monad.State (MonadState(..))
42 | import Control.Monad.Trans (MonadTrans(..))
43 | import Control.Monad.Trans.Control
44 | import Control.Monad.Trans.Resource (MonadResource (..))
45 | import Control.Monad.Writer (MonadWriter(..))
46 | import GHC.Stack
47 | import GHC.Stack.Plus
48 | import Safe.Plus
49 | import Test.QuickCheck
50 | import qualified Control.Monad.Fail
51 | import qualified Data.Text as T
52 |
53 | instance Arbitrary a => Arbitrary (Fail a) where
54 | arbitrary =
55 | oneof
56 | [ Ok <$> arbitrary
57 | , Fail <$> arbitrary
58 | ]
59 |
60 | instance MonadThrow m => MonadThrow (FailT m) where
61 | throwM = FailT . throwM
62 |
63 | instance MonadBase b m => MonadBase b (FailT m) where
64 | liftBase = liftBaseDefault
65 |
66 | instance MonadBaseControl b m => MonadBaseControl b (FailT m) where
67 | type StM (FailT m) a = ComposeSt FailT m a
68 | liftBaseWith = defaultLiftBaseWith
69 | restoreM = defaultRestoreM
70 |
71 | instance MonadTransControl FailT where
72 | type StT FailT a = Fail a
73 | liftWith f = FailT $ return <$> f runFailT
74 | restoreT = FailT
75 |
76 | instance Monad m => MonadError String (FailT m) where
77 | throwError = throwFailT
78 | catchError = catchFailT
79 |
80 | instance MonadTrans FailT where
81 | lift m =
82 | FailT $
83 | do a <- m
84 | return (Ok a)
85 |
86 | instance MonadIO m => MonadIO (FailT m) where
87 | liftIO io = FailT $ fmap Ok (liftIO io)
88 |
89 | instance MonadState s m => MonadState s (FailT m) where
90 | get = lift get
91 | put = lift . put
92 |
93 | instance MonadResource m => MonadResource (FailT m) where
94 | liftResourceT = FailT . fmap Ok . liftResourceT
95 |
96 | instance MonadWriter w m => MonadWriter w (FailT m) where
97 | tell = lift . tell
98 | listen =
99 | mapFailT $ \m ->
100 | do (a, w) <- listen m
101 | return $! fmap (\r -> (r, w)) a
102 | pass =
103 | mapFailT $ \m ->
104 | pass $
105 | do a <- m
106 | return $!
107 | case a of
108 | Err l -> (Err l, id)
109 | Ok (r, f) -> (Ok r, f)
110 |
111 | mapFailT :: (m (Fail a) -> n (Fail b)) -> FailT m a -> FailT n b
112 | mapFailT f = FailT . f . runFailT
113 |
114 | throwFailT :: Monad m => String -> FailT m a
115 | throwFailT l = FailT $ return (Fail l)
116 |
117 | catchFailT :: Monad m => FailT m a -> (String -> FailT m a) -> FailT m a
118 | m `catchFailT` h =
119 | FailT $
120 | do a <- runFailT m
121 | case a of
122 | Err l -> runFailT (h $ T.unpack l)
123 | Ok r -> return (Ok r)
124 |
125 | isFail :: Fail a -> Bool
126 | isFail (Err _) = True
127 | isFail (Ok _) = False
128 |
129 | isOk :: Fail a -> Bool
130 | isOk = not . isFail
131 |
132 | instance Monad Fail where
133 | return = Ok
134 | {-# INLINE return #-}
135 | fail = Control.Monad.Fail.fail
136 | {-# INLINE fail #-}
137 | (>>=) = failBind
138 | {-# INLINE (>>=) #-}
139 |
140 | instance Control.Monad.Fail.MonadFail Fail where
141 | fail = Fail
142 | {-# INLINE fail #-}
143 |
144 | instance MonadPlus Fail where
145 | mzero = failZero
146 | mplus = failPlus
147 |
148 | instance Applicative Fail where
149 | pure = Ok
150 | (<*>) = failAp
151 |
152 | instance Alternative Fail where
153 | empty = failZero
154 | (<|>) = failPlus
155 |
156 | instance MonadFix Fail where
157 | mfix f = let a = f (unOk a) in a
158 | where
159 | unOk (Ok x) = x
160 | unOk (Err msg) = safeError ("mfix failed: " ++ T.unpack msg)
161 |
162 | instance MonadFix m => MonadFix (FailT m) where
163 | mfix f =
164 | FailT $ mfix $ \a -> runFailT $ f $
165 | case a of
166 | Ok r -> r
167 | Err msg -> safeError ("FailT.mfix failed: " ++ T.unpack msg)
168 |
169 | instance Monad m => Monad (FailT m) where
170 | return = returnFailT
171 | fail = Control.Monad.Fail.fail
172 | (>>=) = bindFailT
173 |
174 | instance Monad m => Control.Monad.Fail.MonadFail (FailT m) where
175 | fail = FailT . return . Fail
176 |
177 | instance (Functor m, Monad m) => Applicative (FailT m) where
178 | pure = FailT . return . Ok
179 | FailT f <*> FailT v =
180 | FailT $
181 | do mf <- f
182 | case mf of
183 | Err msg -> return (Err msg)
184 | Ok k ->
185 | do mv <- v
186 | case mv of
187 | Err msg -> return (Err msg)
188 | Ok x -> return (Ok (k x))
189 |
190 | instance Monad m => Alternative (FailT m) where
191 | empty = FailT $ return failZero
192 | FailT f <|> FailT g =
193 | FailT $
194 | do mf <- f
195 | mg <- g
196 | return $ mf `failPlus` mg
197 |
198 | instance Monad m => MonadPlus (FailT m) where
199 | mzero = empty
200 | mplus = (<|>)
201 |
202 | failBind :: Fail a -> (a -> Fail b) -> Fail b
203 | failBind ma f =
204 | case ma of
205 | Ok x -> {-# SCC "Fail/>>=/f" #-} (f x)
206 | -- is there a better way to avoid allocations?
207 | Err x -> {-# SCC "Fail/>>=/Fail" #-} (Err x)
208 | {-# INLINE failBind #-}
209 |
210 | failAp :: Fail (a -> b) -> Fail a -> Fail b
211 | failAp (Ok f) (Ok a) = Ok (f a)
212 | failAp (Err msg) _ = Err msg
213 | failAp _ (Err msg) = Err msg
214 | {-# INLINE failAp #-}
215 |
216 | failZero :: Fail a
217 | failZero = Fail "mzero"
218 | {-# INLINE failZero #-}
219 |
220 | failPlus :: Fail a -> Fail a -> Fail a
221 | failPlus x@(Ok _) _ = x
222 | failPlus _ x = x
223 | {-# INLINE failPlus #-}
224 |
225 | failSwitch :: (String -> c) -> (a -> c) -> Fail a -> c
226 | failSwitch _ g (Ok x) = g x
227 | failSwitch f _ (Err x) = f (T.unpack x)
228 | {-# INLINE failSwitch #-}
229 |
230 | {-# INLINE runFailT #-}
231 | runFailT :: FailT m a -> m (Fail a)
232 | runFailT = unFailT
233 |
234 | {-# INLINE returnFailT #-}
235 | returnFailT :: Monad m => a -> FailT m a
236 | returnFailT = FailT . return . Ok
237 |
238 | {-# INLINE bindFailT #-}
239 | bindFailT :: Monad m => FailT m a -> (a -> FailT m b) -> FailT m b
240 | bindFailT (FailT action) f =
241 | FailT $
242 | do mx <- action
243 | case mx of
244 | Ok x -> unFailT (f x)
245 | Err m -> return (Err m)
246 |
247 | instance MonadError String Fail where
248 | throwError = Fail
249 | Err l `catchError` h = h (T.unpack l)
250 | Ok r `catchError` _ = Ok r
251 |
252 | failMaybe :: String -> Maybe a -> Fail a
253 | failMaybe _ (Just x) = Ok x
254 | failMaybe msg Nothing = Fail msg
255 |
256 | failEitherStr :: Either String a -> Fail a
257 | failEitherStr = either Fail Ok
258 |
259 | failEitherText :: Either T.Text a -> Fail a
260 | failEitherText = either (Fail . T.unpack) Ok
261 |
262 | failEitherShow :: Show a => Either a b -> Fail b
263 | failEitherShow e =
264 | case e of
265 | Left err -> Fail $ show err
266 | Right val -> Ok val
267 |
268 | runExceptTFail :: Monad m => ExceptT String m a -> m (Fail a)
269 | runExceptTFail err =
270 | do eith <- runExceptT err
271 | case eith of
272 | Left err -> return $ Fail err
273 | Right x -> return $ Ok x
274 |
275 | class Control.Monad.Fail.MonadFail m => MonadFailure m where
276 | catchFailure :: m a -> (String -> m a) -> m a
277 |
278 | instance MonadFailure Maybe where
279 | Nothing `catchFailure` hdl = hdl "Failed in Maybe."
280 | ok `catchFailure` _ = ok
281 |
282 | instance MonadFailure IO where
283 | catchFailure action hdl = action `catch` \(ErrorCall s) -> hdl s
284 |
285 | instance MonadFailure Fail where
286 | ok@(Ok _) `catchFailure` _ = ok
287 | Err msg `catchFailure` hdl = hdl (T.unpack msg)
288 |
289 | instance Monad m => MonadFailure (FailT m) where
290 | FailT action `catchFailure` hdl =
291 | FailT $
292 | do result <- action
293 | case result of
294 | Err msg -> unFailT (hdl $ T.unpack msg)
295 | Ok _ -> return result
296 |
297 | instance (MonadFail (ReaderT r m), MonadFailure m) => MonadFailure (ReaderT r m) where
298 | action `catchFailure` handler =
299 | ReaderT $ \r ->
300 | runReaderT action r `catchFailure` \msg -> runReaderT (handler msg) r
301 |
302 | failInM :: Monad m => Fail a -> m a
303 | failInM f = failInM' f id
304 |
305 | failInM' :: Monad m => Fail a -> (String -> String) -> m a
306 | failInM' f h =
307 | case f of
308 | Ok x -> return x
309 | Err msg -> fail (h $ T.unpack msg)
310 |
311 | failInM'' :: Monad m => String -> Fail a -> m a
312 | failInM'' what = flip failInM' (("Failed to " ++ what ++ ":")++)
313 |
314 | mapFail :: (String -> String) -> Fail a -> Fail a
315 | mapFail f x =
316 | case x of
317 | Ok _ -> x
318 | Err msg -> Fail (f $ T.unpack msg)
319 |
320 | failToEither :: Fail a -> Either String a
321 | failToEither (Ok x) = Right x
322 | failToEither (Err x) = Left (T.unpack x)
323 |
324 | failToMaybe :: Fail a -> Maybe a
325 | failToMaybe (Ok x) = Just x
326 | failToMaybe _ = Nothing
327 |
328 | failForIOException :: IO a -> IO (Fail a)
329 | failForIOException action =
330 | catch (Ok <$> action) (\(exc::IOException) -> return (Fail (show exc)))
331 |
332 | catFails :: [Fail a] -> [a]
333 | catFails [] = []
334 | catFails ((Err _):xs) = catFails xs
335 | catFails ((Ok a):xs) = a:(catFails xs)
336 |
337 | fromFail :: (String -> a) -> Fail a -> a
338 | fromFail f = failSwitch f id
339 |
340 | fromFailString :: Fail a -> Maybe String
341 | fromFailString f =
342 | case f of
343 | Ok _ -> Nothing
344 | Err str -> Just (T.unpack str)
345 |
346 | runError :: forall a. (forall m. Monad m => m a) -> Either String a
347 | runError x = runIdentity (runExceptT x)
348 |
349 | partitionFails :: [Fail a] -> ([a], [String])
350 | partitionFails l = go l ([], [])
351 | where
352 | go l (good, bad) =
353 | case l of
354 | [] ->
355 | (reverse good, reverse bad)
356 | (Ok x : rest) ->
357 | go rest (x : good, bad)
358 | (Err s : rest) ->
359 | go rest (good, T.unpack s : bad)
360 |
361 | eitherToError :: MonadError e m => Either e a -> m a
362 | eitherToError = either throwError return
363 |
364 | errorToEither :: MonadError e m => m a -> m (Either e a)
365 | errorToEither m = catchError (Right <$> m) (return . Left)
366 |
367 | errorToDefault :: MonadError e m => a -> m a -> m a
368 | errorToDefault a ma = catchError ma (\_ -> return a)
369 |
370 | liftError :: (MonadError e m, MonadError e m1) => (forall a. m a -> m1 a) -> m a -> m1 a
371 | liftError liftBase action = liftBase (errorToEither action) >>= eitherToError
372 |
373 | errorToMaybe :: MonadError e m => m a -> m (Maybe a)
374 | errorToMaybe ma = catchError (Just <$> ma) (\_ -> return Nothing)
375 |
376 | maybeToError :: MonadError e m => String -> Maybe a -> m a
377 | maybeToError msg ma =
378 | case ma of
379 | Nothing -> safeFail msg
380 | Just a -> return a
381 |
382 | maybeToFail :: Monad m => String -> Maybe a -> m a
383 | maybeToFail msg ma =
384 | case ma of
385 | Nothing -> safeFail msg
386 | Just a -> return a
387 |
388 | eitherToFail :: Monad m => Either String a -> m a
389 | eitherToFail = either safeFail return
390 |
391 | runExceptTorFail :: (Monad m, Show e) => ExceptT e m a -> m a
392 | runExceptTorFail action =
393 | do result <- runExceptT action
394 | either (safeFail . show) return result
395 |
396 | safeFromOk :: (HasCallStack) => Fail a -> a
397 | safeFromOk f =
398 | case f of
399 | Ok x -> x
400 | Err msg -> safeError $ callerLocation ++ ": Fail " ++ show msg
401 |
--------------------------------------------------------------------------------
/strict-data/src/Data/Fail/Types.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP #-}
2 | {-# LANGUAGE DeriveFoldable #-}
3 | {-# LANGUAGE DeriveTraversable #-}
4 | {-# LANGUAGE MultiParamTypeClasses #-}
5 | {-# LANGUAGE PatternSynonyms #-}
6 | {-# LANGUAGE ViewPatterns #-}
7 | module Data.Fail.Types
8 | ( Fail(..), pattern Fail
9 | , FailT(..)
10 | , FIO
11 | )
12 | where
13 |
14 | import qualified Data.Text as T
15 |
16 | pattern Fail :: String -> Fail a
17 | pattern Fail x <- Err (T.unpack -> x) where
18 | Fail x = Err (T.pack x)
19 |
20 | #if (MIN_VERSION_base(4,10,0))
21 | {-# COMPLETE Ok, Fail #-}
22 | #endif
23 |
24 | data Fail a
25 | = Err T.Text
26 | | Ok !a
27 | deriving (Show, Ord, Eq, Functor, Foldable, Traversable)
28 |
29 | newtype FailT m a = FailT { unFailT :: m (Fail a) }
30 | deriving (Functor)
31 |
32 | type FIO a = FailT IO a
33 |
--------------------------------------------------------------------------------
/strict-data/src/Data/Map/Ordered.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ExistentialQuantification #-}
2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
3 | {-# LANGUAGE DeriveDataTypeable #-}
4 | {-# LANGUAGE DeriveFoldable #-}
5 | {-# LANGUAGE BangPatterns #-}
6 | -- | An ordered, strict map.
7 | --
8 | -- One might think that `Data.Map.Strict` already provides such a data type. This is not correct.
9 | -- `Data.Map.Lazy` and `Data.Map.Strict` use the same, non-strict `Map` datatype.
10 | -- `Data.Map.Strict` just provides functions that evaluate the value argument before inserting it
11 | -- in the Map. The problem is that the typeclass instances of the shared `Map` datatype use the
12 | -- non-strict functions.
13 | module Data.Map.Ordered
14 | ( OSMap, Map, empty, lookup, insert, delete, fromList, fromListWith, toList, map, mapMaybe
15 | , lookupLT, lookupGT, lookupLE, lookupGE, lookupM, elemAt
16 | , singleton, insertWith
17 | , member, elems, unionWith, difference, union, findWithDefault, size, null, isSubmapOf, unions
18 | , intersection, foldrWithKey, foldlWithKey, filter, filterWithKey
19 | , keys, toDescList, updateLookupWithKey
20 | , deleteLookup, insertLookupWithKey, adjust, assocs, insertWith'
21 | , alter, differenceWith, updateWithKey, update, mapKeys, insertWithKey, insertWithKey'
22 | , keysSet
23 | , maxView, maxViewWithKey, minView, minViewWithKey
24 | , intersectionWith, fromDistinctAscList
25 | , toDataMap, fromDataMap, hasKey, hasValue
26 | )
27 | where
28 |
29 | import Control.Arrow (second)
30 | import Control.DeepSeq (NFData(..))
31 | import Data.Coerce
32 | import Data.Data
33 | import Data.Hashable
34 | import Data.List (foldl')
35 | import Data.Maybe (isJust)
36 | import Prelude hiding (map, lookup, null, filter)
37 | import Test.QuickCheck
38 | import qualified Data.Map.Strict as DM
39 | import qualified Data.Set as Set
40 |
41 | type Map = OSMap
42 |
43 | newtype OSMap k v = OSMap { unOSMap :: DM.Map k v }
44 | deriving (Eq, Ord, Read, Show, Foldable, NFData, Data)
45 |
46 | instance (Hashable k, Hashable v) => Hashable (OSMap k v) where
47 | hashWithSalt = foldlWithKey updateHash
48 | where
49 | updateHash salt k v = hashWithSalt salt k `hashWithSalt` v
50 |
51 | instance Functor (OSMap k) where
52 | {-# INLINE fmap #-}
53 | fmap = map
54 |
55 | instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (OSMap k v) where
56 | arbitrary = OSMap <$> arbitrary
57 | shrink = coerce . shrink . unOSMap
58 |
59 | instance Traversable (OSMap k) where
60 | {-# INLINE traverse #-}
61 | traverse f (OSMap m) =
62 | fromDataMap <$> DM.traverseWithKey (\_ x -> let y = f x in y) m
63 |
64 | instance (Ord k) => Monoid (OSMap k v) where
65 | mempty = empty
66 | mconcat = unions
67 | mappend = union
68 |
69 | {-# INLINE fromDataMap #-}
70 | fromDataMap :: DM.Map k v -> OSMap k v
71 | fromDataMap dm = DM.foldr (\a b -> a `seq` b) () dm `seq` OSMap dm
72 |
73 | {-# INLINE toDataMap #-}
74 | toDataMap :: OSMap k v -> DM.Map k v
75 | toDataMap = unOSMap
76 |
77 | {-# INLINE empty #-}
78 | empty :: OSMap k v
79 | empty = OSMap DM.empty
80 |
81 | {-# INLINE member #-}
82 | member :: (Ord k) => k -> OSMap k v -> Bool
83 | member k (OSMap m) = DM.member k m
84 |
85 | {-# INLINE lookup #-}
86 | lookup :: (Ord k) => k -> OSMap k v -> Maybe v
87 | lookup k (OSMap hm) = DM.lookup k hm
88 |
89 | {-# INLINE lookupM #-}
90 | lookupM :: (Show k, Ord k, Monad m) => k -> OSMap k v -> m v
91 | lookupM k (OSMap hm) =
92 | case DM.lookup k hm of
93 | Nothing -> fail ("Could not find " ++ show k ++ " in Map.")
94 | Just x -> pure x
95 |
96 | {-# INLINE lookupLT #-}
97 | lookupLT :: (Ord k) => k -> OSMap k v -> Maybe (k, v)
98 | lookupLT k (OSMap hm) = DM.lookupLT k hm
99 |
100 | {-# INLINE lookupGT #-}
101 | lookupGT :: (Ord k) => k -> OSMap k v -> Maybe (k, v)
102 | lookupGT k (OSMap hm) = DM.lookupGT k hm
103 |
104 | {-# INLINE lookupLE #-}
105 | lookupLE :: (Ord k) => k -> OSMap k v -> Maybe (k, v)
106 | lookupLE k (OSMap hm) = DM.lookupLE k hm
107 |
108 | {-# INLINE lookupGE #-}
109 | lookupGE :: (Ord k) => k -> OSMap k v -> Maybe (k, v)
110 | lookupGE k (OSMap hm) = DM.lookupGE k hm
111 |
112 | {-# INLINE insert #-}
113 | insert :: (Ord k) => k -> v -> OSMap k v -> OSMap k v
114 | insert k v (OSMap hm) = OSMap (DM.insert k v hm)
115 |
116 | {-# INLINE delete #-}
117 | delete :: (Ord k) => k -> OSMap k v -> OSMap k v
118 | delete k (OSMap hm) = OSMap (DM.delete k hm)
119 |
120 | {-# INLINE fromList #-}
121 | fromList :: (Ord k) => [(k,v)] -> OSMap k v
122 | fromList = OSMap . DM.fromList
123 |
124 | {-# INLINE fromListWith #-}
125 | fromListWith :: (Ord k) => (v -> v -> v) -> [(k,v)] -> OSMap k v
126 | fromListWith f kvs = OSMap $ DM.fromListWith f kvs
127 |
128 | {-# INLINE toList #-}
129 | toList :: OSMap k v -> [(k, v)]
130 | toList (OSMap hm) = DM.toList hm
131 |
132 | {-# INLINE toDescList #-}
133 | toDescList :: OSMap k v -> [(k, v)]
134 | toDescList (OSMap hm) = DM.toDescList hm
135 |
136 | {-# INLINE map #-}
137 | map :: (v -> v') -> OSMap k v -> OSMap k v'
138 | map f (OSMap m) = OSMap (DM.map f m)
139 |
140 | {-# INLINE mapMaybe #-}
141 | mapMaybe :: (v -> Maybe v') -> OSMap k v -> OSMap k v'
142 | mapMaybe f (OSMap m) = OSMap (DM.mapMaybe f m)
143 |
144 | {-# INLINE singleton #-}
145 | singleton :: k -> v -> OSMap k v
146 | singleton k v = OSMap (DM.singleton k v)
147 |
148 | {-# INLINE insertWith #-}
149 | insertWith :: (Ord k) => (v -> v -> v) -> k -> v -> OSMap k v -> OSMap k v
150 | insertWith f k !v (OSMap hm) = OSMap (DM.insertWith f k v hm)
151 |
152 | {-# INLINE elems #-}
153 | elems :: OSMap k v -> [v]
154 | elems = DM.elems . unOSMap
155 |
156 | {-# INLINE keys #-}
157 | keys :: OSMap k v -> [k]
158 | keys = DM.keys . unOSMap
159 |
160 | {-# INLINE keysSet #-}
161 | keysSet :: OSMap k v -> Set.Set k
162 | keysSet = DM.keysSet . unOSMap
163 |
164 | {-# INLINE union #-}
165 | union :: Ord k => OSMap k v -> OSMap k v -> OSMap k v
166 | union (OSMap m1) (OSMap m2) = OSMap (DM.union m1 m2)
167 |
168 | {-# INLINABLE unions #-}
169 | unions :: Ord k => [OSMap k v] -> OSMap k v
170 | unions ts = foldl' union empty ts
171 |
172 | {-# INLINE unionWith #-}
173 | unionWith :: Ord k => (v -> v -> v) -> OSMap k v -> OSMap k v -> OSMap k v
174 | unionWith f (OSMap m1) (OSMap m2) = OSMap (DM.unionWith f m1 m2)
175 |
176 | {-# INLINE difference #-}
177 | difference :: (Ord k) => OSMap k v -> OSMap k w -> OSMap k v
178 | difference (OSMap m1) (OSMap m2) = OSMap (DM.difference m1 m2)
179 |
180 | {-# INLINE intersection #-}
181 | intersection :: (Ord k) => OSMap k v -> OSMap k w -> OSMap k v
182 | intersection (OSMap m1) (OSMap m2) = OSMap (DM.intersection m1 m2)
183 |
184 | {-# INLINE findWithDefault #-}
185 | findWithDefault :: (Ord k) => a -> k -> OSMap k a -> a
186 | findWithDefault def k (OSMap m) = DM.findWithDefault def k m
187 |
188 | {-# INLINE elemAt #-}
189 | elemAt :: Int -> OSMap k a -> (k, a)
190 | elemAt n (OSMap m) = DM.elemAt n m
191 |
192 | {-# INLINE size #-}
193 | size :: OSMap k v -> Int
194 | size = DM.size . unOSMap
195 |
196 | {-# INLINE null #-}
197 | null :: OSMap k v -> Bool
198 | null = DM.null . unOSMap
199 |
200 | {-# INLINE isSubmapOf #-}
201 | isSubmapOf :: (Ord k, Eq a) => OSMap k a -> OSMap k a -> Bool
202 | isSubmapOf (OSMap a) (OSMap b) = DM.isSubmapOf a b
203 |
204 | {-# INLINE foldrWithKey #-}
205 | foldrWithKey :: (k -> v -> a -> a) -> a -> OSMap k v -> a
206 | foldrWithKey f a (OSMap hm) = DM.foldrWithKey f a hm
207 |
208 | {-# INLINE foldlWithKey #-}
209 | foldlWithKey :: (a -> k -> v -> a) -> a -> OSMap k v -> a
210 | foldlWithKey f a (OSMap hm) = DM.foldlWithKey f a hm
211 |
212 | {-# INLINE filter #-}
213 | filter :: (v -> Bool) -> OSMap k v -> OSMap k v
214 | filter f (OSMap m) = OSMap (DM.filter f m)
215 |
216 | {-# INLINE filterWithKey #-}
217 | filterWithKey :: (k -> v -> Bool) -> OSMap k v -> OSMap k v
218 | filterWithKey f (OSMap m) = OSMap (DM.filterWithKey f m)
219 |
220 | {-# INLINE updateLookupWithKey #-}
221 | updateLookupWithKey :: Ord k => (k -> a -> Maybe a) -> k -> OSMap k a -> (Maybe a, OSMap k a)
222 | updateLookupWithKey f k (OSMap curMap) =
223 | let (mv, newMap) = DM.updateLookupWithKey f k curMap
224 | in (mv, OSMap newMap)
225 |
226 | {-# INLINE deleteLookup #-}
227 | deleteLookup :: Ord k => k -> OSMap k v -> (Maybe v, OSMap k v)
228 | deleteLookup = updateLookupWithKey (\_k _v -> Nothing)
229 |
230 | {-# INLINE insertLookupWithKey #-}
231 | insertLookupWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> OSMap k a -> (Maybe a, OSMap k a)
232 | insertLookupWithKey f k !newV (OSMap curM) =
233 | let (mOldV, newM) = DM.insertLookupWithKey f k newV curM
234 | in (mOldV, OSMap newM)
235 |
236 | {-# INLINE adjust #-}
237 | adjust :: Ord k => (a -> a) -> k -> Map k a -> Map k a
238 | adjust f k = OSMap . DM.adjust f k . unOSMap
239 |
240 | {-# INLINE assocs #-}
241 | assocs :: OSMap k a -> [(k, a)]
242 | assocs = DM.assocs . unOSMap
243 |
244 | {-# INLINE insertWith' #-}
245 | insertWith' :: Ord k => (a -> a -> a) -> k -> a -> OSMap k a -> OSMap k a
246 | insertWith' f k !v (OSMap dm) = OSMap (DM.insertWith f k v dm)
247 |
248 | {-# INLINE alter #-}
249 | alter :: Ord k => (Maybe a -> Maybe a) -> k -> OSMap k a -> OSMap k a
250 | alter f k (OSMap dm) = OSMap (DM.alter f k dm)
251 |
252 | {-# INLINE differenceWith #-}
253 | differenceWith :: Ord k => (a -> b -> Maybe a) -> OSMap k a -> OSMap k b -> OSMap k a
254 | differenceWith f (OSMap dmA) (OSMap dmB) = OSMap (DM.differenceWith f dmA dmB)
255 |
256 | {-# INLINE updateWithKey #-}
257 | updateWithKey :: Ord k => (k -> a -> Maybe a) -> k -> OSMap k a -> OSMap k a
258 | updateWithKey f k (OSMap dm) = OSMap (DM.updateWithKey f k dm)
259 |
260 | {-# INLINE update #-}
261 | update :: Ord k => (a -> Maybe a) -> k -> OSMap k a -> OSMap k a
262 | update f k (OSMap dm) = OSMap (DM.update f k dm)
263 |
264 | {-# INLINE insertWithKey #-}
265 | insertWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> Map k a
266 | insertWithKey f k !v (OSMap dm) = OSMap (DM.insertWithKey f k v dm)
267 |
268 | {-# INLINE insertWithKey' #-}
269 | insertWithKey' :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> Map k a
270 | insertWithKey' = insertWithKey
271 |
272 | {-# INLINE mapKeys #-}
273 | mapKeys :: Ord k2 => (k1 -> k2) -> OSMap k1 a -> OSMap k2 a
274 | mapKeys f (OSMap dm) = OSMap (DM.mapKeys f dm)
275 |
276 | {-# INLINE maxView #-}
277 | maxView :: OSMap k a -> Maybe (a, OSMap k a)
278 | maxView (OSMap m) = fmap (second OSMap) (DM.maxView m)
279 |
280 | {-# INLINE maxViewWithKey #-}
281 | maxViewWithKey :: OSMap k a -> Maybe ((k, a), OSMap k a)
282 | maxViewWithKey (OSMap m) = fmap (second OSMap) (DM.maxViewWithKey m)
283 |
284 | {-# INLINE minView #-}
285 | minView :: OSMap k a -> Maybe (a, OSMap k a)
286 | minView (OSMap m) = fmap (second OSMap) (DM.minView m)
287 |
288 | {-# INLINE minViewWithKey #-}
289 | minViewWithKey :: OSMap k a -> Maybe ((k, a), OSMap k a)
290 | minViewWithKey = fmap (second OSMap) . DM.minViewWithKey . unOSMap
291 |
292 | {-# INLINE intersectionWith #-}
293 | intersectionWith :: Ord k => (a -> b -> c) -> OSMap k a -> OSMap k b -> OSMap k c
294 | intersectionWith f (OSMap l) (OSMap r) =
295 | OSMap (DM.intersectionWith f l r)
296 |
297 | {-# INLINE fromDistinctAscList #-}
298 | fromDistinctAscList :: [(k,v)] -> OSMap k v
299 | fromDistinctAscList = OSMap . DM.fromDistinctAscList
300 |
301 | hasValue :: Int -> OSMap Int Int -> Bool
302 | hasValue v m = any (\(_, x) -> x == v) (toList m)
303 |
304 | hasKey :: Int -> OSMap Int Int -> Bool
305 | hasKey k m = isJust (lookup k m)
306 |
--------------------------------------------------------------------------------
/strict-data/src/Data/Map/Unordered.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE BangPatterns #-}
2 | {-# LANGUAGE DeriveTraversable #-}
3 | {-# LANGUAGE DeriveFunctor #-}
4 | {-# LANGUAGE DeriveDataTypeable #-}
5 | {-# LANGUAGE DeriveFoldable #-}
6 | {-# LANGUAGE ExistentialQuantification #-}
7 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
8 | {-# LANGUAGE ScopedTypeVariables #-}
9 | -- | An unordered, strict map.
10 | module Data.Map.Unordered
11 | ( USMap, Map, empty, lookup, insert, delete, fromList, toList, map, singleton, insertWith
12 | , member, elems, unionWith, difference, union, findWithDefault, size, null, isSubmapOf
13 | , intersection, foldrWithKey, foldlWithKey, foldlWithKey', keys, insertLookupWithKey
14 | , updateLookupWithKey, adjust, deleteLookup, assocs, insertWith', update, alter
15 | , lookup', unions, toHashMap, fromHashMap, filter, filterWithKey, keysSet, lookupDefault
16 | , fromListWith, mapMaybe, unionsWith
17 | )
18 | where
19 |
20 |
21 | import Control.DeepSeq (NFData(..))
22 | import Data.Data
23 | import Data.Hashable (Hashable(..))
24 | import Data.Maybe (isJust)
25 | import Prelude hiding (map, lookup, null, filter, pred)
26 | import Test.QuickCheck (Arbitrary(..))
27 | import qualified Data.HashMap.Strict as HM
28 | import qualified Data.HashSet as Set
29 | import qualified Data.List as List
30 |
31 |
32 | type Map = USMap
33 |
34 | newtype USMap k v = USMap { unUSMap :: HM.HashMap k v }
35 | deriving (Eq, Functor, Foldable, Traversable, Typeable, Data, Monoid)
36 |
37 | instance (Hashable k, Eq k, Read k, Read v) => Read (USMap k v) where
38 | readsPrec p s =
39 | do (l, r) <- readsPrec p s
40 | return (fromList l, r)
41 |
42 | instance (Show k, Show v) => Show (USMap k v) where
43 | showsPrec p usmap = showsPrec p (toList usmap)
44 |
45 | instance (Hashable k, Hashable v) => Hashable (USMap k v) where
46 | hashWithSalt s (USMap hm) = hashWithSalt s hm
47 |
48 | instance (NFData k, NFData v) => NFData (USMap k v) where
49 | rnf (USMap x) = rnf x
50 |
51 | instance (Hashable k, Eq k, Arbitrary k, Arbitrary v) => Arbitrary (USMap k v) where
52 | arbitrary = fromList <$> arbitrary
53 |
54 | toHashMap :: USMap k v -> HM.HashMap k v
55 | toHashMap = unUSMap
56 |
57 | fromHashMap :: HM.HashMap k v -> USMap k v
58 | fromHashMap = USMap
59 |
60 | empty :: USMap k v
61 | empty = USMap HM.empty
62 |
63 | member :: (Eq k, Hashable k) => k -> USMap k v -> Bool
64 | member k (USMap m) =
65 | case HM.lookup k m of
66 | Just _ -> True
67 | Nothing -> False
68 |
69 | lookupDefault :: (Eq k, Hashable k) => v -> k -> USMap k v -> v
70 | lookupDefault d k (USMap hm) = HM.lookupDefault d k hm
71 |
72 | {-# SPECIALISE lookup :: (Eq k, Hashable k, Show k) => k -> USMap k v -> Maybe v #-}
73 | {-# INLINEABLE lookup #-}
74 | lookup :: (Eq k, Show k, Hashable k, Monad m) => k -> USMap k v -> m v
75 | lookup k (USMap hm) =
76 | case HM.lookup k hm of
77 | Nothing -> fail ("Key " ++ show k ++ " not found.")
78 | Just x -> return x
79 |
80 | {-# INLINE lookup' #-}
81 | lookup' :: (Eq k, Hashable k) => k -> USMap k v -> Maybe v
82 | lookup' k (USMap hm) = HM.lookup k hm
83 |
84 | insert :: (Eq k, Hashable k) => k -> v -> USMap k v -> USMap k v
85 | insert k v (USMap hm) = USMap (HM.insert k v hm)
86 |
87 | delete :: (Eq k, Hashable k) => k -> USMap k v -> USMap k v
88 | delete k (USMap hm) = USMap (HM.delete k hm)
89 |
90 | fromList :: (Eq k, Hashable k) => [(k,v)] -> USMap k v
91 | fromList = USMap . HM.fromList
92 |
93 | fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> USMap k v
94 | fromListWith f = USMap . HM.fromListWith f
95 |
96 | toList :: USMap k v -> [(k, v)]
97 | toList (USMap hm) = HM.toList hm
98 |
99 | map :: (v -> v') -> USMap k v -> USMap k v'
100 | map f = USMap . HM.map f . unUSMap
101 |
102 | mapMaybe :: (v -> Maybe v') -> USMap k v -> USMap k v'
103 | mapMaybe f = USMap . HM.mapMaybe f . unUSMap
104 |
105 | singleton :: Hashable k => k -> v -> USMap k v
106 | singleton k = USMap . HM.singleton k
107 |
108 | insertWith :: (Eq k, Hashable k) => (v -> v -> v) -> k -> v -> USMap k v -> USMap k v
109 | insertWith f k v (USMap hm) = USMap (HM.insertWith f k v hm)
110 |
111 | elems :: USMap k v -> [v]
112 | elems = HM.elems . unUSMap
113 |
114 | keys :: USMap k v -> [k]
115 | keys = HM.keys . unUSMap
116 |
117 | keysSet :: (Eq k, Hashable k) => USMap k v -> Set.HashSet k
118 | keysSet = Set.fromList . fmap fst . toList
119 |
120 | union :: (Hashable k, Eq k) => USMap k v -> USMap k v -> USMap k v
121 | union (USMap m1) (USMap m2) = USMap (HM.union m1 m2)
122 |
123 | unionWith :: (Hashable k, Eq k) => (v -> v -> v) -> USMap k v -> USMap k v -> USMap k v
124 | unionWith f (USMap m1) (USMap m2) = USMap (HM.unionWith f m1 m2)
125 |
126 | difference :: (Eq k, Hashable k) => USMap k v -> USMap k w -> USMap k v
127 | difference (USMap m1) (USMap m2) = USMap (HM.difference m1 m2)
128 |
129 | intersection :: (Eq k, Hashable k) => USMap k v -> USMap k w -> USMap k v
130 | intersection (USMap m1) (USMap m2) = USMap (HM.intersection m1 m2)
131 |
132 | findWithDefault :: (Eq k, Hashable k) => a -> k -> USMap k a -> a
133 | findWithDefault def k (USMap m) =
134 | case HM.lookup k m of
135 | Just v -> v
136 | Nothing -> def
137 |
138 | size :: USMap k v -> Int
139 | size = HM.size . unUSMap
140 |
141 | null :: USMap k v -> Bool
142 | null = HM.null . unUSMap
143 |
144 | isSubmapOf :: (Hashable k, Eq k) => USMap k a -> USMap k a -> Bool
145 | isSubmapOf a b = null (a `difference` b)
146 |
147 | foldrWithKey :: (k -> v -> a -> a) -> a -> USMap k v -> a
148 | foldrWithKey f a (USMap hm) = HM.foldrWithKey f a hm
149 |
150 | {-# WARNING foldlWithKey "This function is strict. Better explicitly use USMap.foldlWithKey'" #-}
151 | foldlWithKey :: (a -> k -> v -> a) -> a -> USMap k v -> a
152 | foldlWithKey f a (USMap hm) = HM.foldlWithKey' f a hm
153 |
154 | foldlWithKey' :: (a -> k -> v -> a) -> a -> USMap k v -> a
155 | foldlWithKey' f a (USMap hm) = HM.foldlWithKey' f a hm
156 |
157 | filterWithKey :: (k -> v -> Bool) -> USMap k v -> USMap k v
158 | filterWithKey pred (USMap hm) = USMap $! HM.filterWithKey pred hm
159 |
160 | filter :: (v -> Bool) -> USMap k v -> USMap k v
161 | filter pred (USMap hm) = USMap $! HM.filter pred hm
162 |
163 | insertLookupWithKey :: (Eq k, Hashable k) => (k -> a -> a -> a) -> k -> a -> USMap k a
164 | -> (Maybe a, USMap k a)
165 | insertLookupWithKey f k newV m =
166 | case lookup' k m of
167 | justV@(Just oldV) -> (justV, insert k (f k newV oldV) m)
168 | nothing@Nothing -> (nothing, insert k newV m)
169 |
170 | updateLookupWithKey :: (Eq k, Hashable k) => (k -> a -> Maybe a) -> k -> USMap k a
171 | -> (Maybe a, USMap k a)
172 | updateLookupWithKey f k m =
173 | case lookup' k m of
174 | Just oldV ->
175 | case f k oldV of
176 | justV@(Just newV) -> (justV, insert k newV m)
177 | Nothing -> (Just oldV, delete k m)
178 | Nothing -> (Nothing, m)
179 |
180 | adjust :: (Eq k, Hashable k) => (a -> a) -> k -> USMap k a -> USMap k a
181 | adjust f k (USMap hm) = USMap (HM.adjust f k hm)
182 |
183 | deleteLookup :: (Eq k, Hashable k) => k -> USMap k a -> (Maybe a, USMap k a)
184 | deleteLookup k m = (lookup' k m, delete k m)
185 |
186 | {-# INLINE assocs #-}
187 | assocs :: USMap k a -> [(k, a)]
188 | assocs = HM.toList . unUSMap
189 |
190 | {-# INLINE insertWith' #-}
191 | insertWith' :: (Eq k, Hashable k) => (a -> a -> a) -> k -> a -> USMap k a -> USMap k a
192 | insertWith' f k !v (USMap hm) = USMap (HM.insertWith f k v hm)
193 |
194 | {-# INLINE update #-}
195 | update :: (Eq k, Hashable k) => (a -> Maybe a) -> k -> USMap k a -> USMap k a
196 | update f k um@(USMap hm) =
197 | case HM.lookup k hm of
198 | Just curV ->
199 | case f curV of
200 | Nothing -> USMap (HM.delete k hm)
201 | Just newV -> USMap (HM.insert k newV hm)
202 | Nothing -> um
203 |
204 | {-# INLINE alter #-}
205 | alter :: (Eq k, Hashable k) => (Maybe a -> Maybe a) -> k -> USMap k a -> USMap k a
206 | alter f k um@(USMap hm) =
207 | let mOld = HM.lookup k hm
208 | mNew = f mOld
209 | in case mNew of
210 | Nothing
211 | | isJust mOld -> USMap (HM.delete k hm)
212 | | otherwise -> um
213 | Just new -> USMap (HM.insert k new hm)
214 |
215 | unions :: (Hashable k, Eq k) => [USMap k a] -> USMap k a
216 | unions = List.foldl' union empty
217 |
218 | unionsWith :: (Hashable k, Eq k) => (a -> a -> a) -> [USMap k a] -> USMap k a
219 | unionsWith f = List.foldl' (unionWith f) empty
220 |
--------------------------------------------------------------------------------
/strict-data/src/Data/Option.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveDataTypeable #-}
2 | {-# LANGUAGE DeriveFoldable #-}
3 | {-# LANGUAGE DeriveFunctor #-}
4 | {-# LANGUAGE DeriveGeneric #-}
5 | {-# LANGUAGE DeriveTraversable #-}
6 | module Data.Option where
7 |
8 | import Data.Fail
9 | import Data.StrictList.Types
10 |
11 | import Control.Applicative
12 | import Control.DeepSeq
13 | import Control.Monad
14 | import Control.Monad.Trans
15 | import Data.Aeson
16 | import Data.Data
17 | import Data.Hashable
18 | import GHC.Generics (Generic)
19 | import Safe.Plus
20 | import Test.QuickCheck
21 | import qualified Control.Monad.Fail as Fail
22 |
23 | data Option a
24 | = None
25 | | Some !a
26 | deriving (Show, Read, Eq, Generic, Typeable, Data, Functor, Foldable, Traversable)
27 |
28 | instance Applicative Option where
29 | pure = Some
30 | {-# INLINE pure #-}
31 |
32 | f <*> x =
33 | case f of
34 | Some g -> fmap g x
35 | None -> None
36 | {-# INLINE (<*>) #-}
37 |
38 | instance Monad Option where
39 | (Some x) >>= k = k x
40 | None >>= _ = None
41 | (>>) = (*>)
42 | fail _ = None
43 |
44 | instance Monoid a => Monoid (Option a) where
45 | mempty = None
46 | None `mappend` m = m
47 | m `mappend` None = m
48 | Some m1 `mappend` Some m2 = Some (m1 `mappend` m2)
49 |
50 | instance Alternative Option where
51 | empty = None
52 | None <|> r = r
53 | l <|> _ = l
54 |
55 | instance MonadPlus Option
56 |
57 | instance ToJSON a => ToJSON (Option a) where
58 | toJSON = toJSON . optionToMaybe
59 | {-# INLINE toJSON #-}
60 |
61 | instance FromJSON a => FromJSON (Option a) where
62 | parseJSON x = maybeToOption <$> parseJSON x
63 | {-# INLINE parseJSON #-}
64 |
65 | newtype OptionT m a
66 | = OptionT
67 | { runOptionT :: m (Option a)
68 | }
69 |
70 | runOptionTDef :: Functor m => a -> OptionT m a -> m a
71 | runOptionTDef x = fmap (fromOption x) . runOptionT
72 |
73 | class ToOptionT t where
74 | optionT :: Monad m => m (t a) -> OptionT m a
75 |
76 | instance ToOptionT Maybe where
77 | optionT = OptionT . fmap maybeToOption
78 |
79 | instance ToOptionT Option where
80 | optionT = OptionT
81 |
82 | instance Functor m => Functor (OptionT m) where
83 | fmap f = OptionT . fmap (fmap f) . runOptionT
84 |
85 | instance (Functor m, Monad m) => Applicative (OptionT m) where
86 | pure = return
87 | (<*>) = ap
88 |
89 | instance Monad m => Fail.MonadFail (OptionT m) where
90 | fail _ = OptionT (return None)
91 |
92 | instance Monad m => Monad (OptionT m) where
93 | fail = safeFail
94 | return = lift . return
95 | x >>= f = OptionT (runOptionT x >>= option (return None) (runOptionT . f))
96 |
97 | instance Ord a => Ord (Option a) where
98 | compare x y =
99 | case x of
100 | Some a ->
101 | case y of
102 | Some b -> compare a b
103 | None -> GT
104 | None ->
105 | case y of
106 | None -> EQ
107 | Some _ -> LT
108 |
109 | instance NFData a => NFData (Option a) where
110 | rnf None = ()
111 | rnf (Some b) = rnf b
112 |
113 | instance MonadTrans OptionT where
114 | lift x = OptionT (Some <$> x)
115 |
116 | instance (MonadIO m) => MonadIO (OptionT m) where
117 | liftIO = lift . liftIO
118 |
119 | instance Fail.MonadFail Option where
120 | fail _ = None
121 |
122 | instance Arbitrary a => Arbitrary (Option a) where
123 | arbitrary = frequency [(1, return None), (3, Some <$> arbitrary)]
124 |
125 | shrink (Some x) = None : [ Some x' | x' <- shrink x ]
126 | shrink _ = []
127 |
128 | noneIf :: (a -> Bool) -> a -> Option a
129 | noneIf p x
130 | | p x = None
131 | | otherwise = Some x
132 |
133 | fromOption :: a -> Option a -> a
134 | fromOption def opt =
135 | case opt of
136 | Some x -> x
137 | None -> def
138 |
139 | isSome :: Option a -> Bool
140 | isSome (Some _) = True
141 | isSome _ = False
142 |
143 | isNone :: Option a -> Bool
144 | isNone None = True
145 | isNone _ = False
146 |
147 | optionToMaybe :: Option a -> Maybe a
148 | optionToMaybe (Some a) = Just a
149 | optionToMaybe None = Nothing
150 | {-# INLINE optionToMaybe #-}
151 |
152 | -- |
153 | -- prop> maybeToOption (optionToMaybe x) == x
154 | maybeToOption :: Maybe a -> Option a
155 | maybeToOption (Just a) = Some a
156 | maybeToOption Nothing = None
157 | {-# INLINE maybeToOption #-}
158 |
159 | optionToList :: Option a -> [a]
160 | optionToList (Some a) = [a]
161 | optionToList None = []
162 |
163 | optionToSL :: Option a -> StrictList a
164 | optionToSL (Some a) = a :! Nil
165 | optionToSL None = Nil
166 |
167 | listToOption :: [a] -> Option a
168 | listToOption [] = None
169 | listToOption (x:_) = Some x
170 |
171 | getSomeNote :: Monad m => String -> Option a -> m a
172 | getSomeNote str = option (safeFail str) return
173 |
174 | option :: b -> (a -> b) -> Option a -> b
175 | option def f opt =
176 | case opt of
177 | Some a -> f $! a
178 | None -> def
179 |
180 | catOptions :: [Option a] -> [a]
181 | catOptions ls = [x | Some x <- ls]
182 |
183 | mapOption :: (a -> Option b) -> [a] -> [b]
184 | mapOption _ [] = []
185 | mapOption f (x:xs) =
186 | let rs = mapOption f xs in
187 | case f x of
188 | None -> rs
189 | Some r -> r : rs
190 |
191 | instance Hashable a => Hashable (Option a)
192 |
193 | forOptionM :: Monad m => [a] -> (a -> OptionT m b) -> m [b]
194 | forOptionM xs f = catOptions <$> forM xs (runOptionT . f)
195 |
196 | mapOptionM :: Monad m => (a -> OptionT m b) -> [a] -> m [b]
197 | mapOptionM = flip forOptionM
198 |
199 | safeFromSome :: Option a -> a
200 | safeFromSome = fromOption (safeError "fromSome is None!")
201 |
202 | failToOption :: Fail a -> Option a
203 | failToOption (Ok x) = Some x
204 | failToOption _ = None
205 |
206 | optionToFail :: String -> Option a -> Fail a
207 | optionToFail _ (Some x) = Ok x
208 | optionToFail err None = Fail err
209 |
210 | optionToFailT :: Monad m => String -> Option a -> FailT m a
211 | optionToFailT _ (Some x) = return x
212 | optionToFailT err None = safeFail err
213 |
--------------------------------------------------------------------------------
/strict-data/src/Data/StrictList.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2 | {-# LANGUAGE BangPatterns #-}
3 | {-# LANGUAGE MonadComprehensions #-}
4 | {-# LANGUAGE ScopedTypeVariables #-}
5 | {-# LANGUAGE TypeFamilies #-}
6 | {-# LANGUAGE TypeOperators #-}
7 | {-# LANGUAGE ViewPatterns #-}
8 | module Data.StrictList
9 | ( StrictList(..)
10 | , SL
11 | , (+!+)
12 | , (\!\)
13 | , all
14 | , any
15 | , atIdx
16 | , break
17 | , catMaybes
18 | , catOptions
19 | , catOptionsL
20 | , concat
21 | , concatSL
22 | , concatMap
23 | , concatMapSL
24 | , concatMapM
25 | , concatText
26 | , delete
27 | , deleteBy
28 | , deleteIdx
29 | , drop
30 | , dropWhile
31 | , dropWhileEnd
32 | , elem
33 | , filter
34 | , find
35 | , findIndex
36 | , fromLazyList, toLazyList
37 | , groupBy
38 | , headM
39 | , headOpt
40 | , insert
41 | , insertBy
42 | , intercalateString
43 | , intercalateText
44 | , intersperse
45 | , lastM
46 | , lastOpt
47 | , length
48 | , ll
49 | , lookup
50 | , lookupM
51 | , lookupM'
52 | , lookupM''
53 | , map
54 | , mapM
55 | , mapM_
56 | , mapMaybe
57 | , mapOption
58 | , maximumM
59 | , maybeToStrictList
60 | , mconcatSL
61 | , notElem
62 | , nub
63 | , null
64 | , optionToStrictList
65 | , partition
66 | , replicate
67 | , reverse
68 | , singleton
69 | , sl
70 | , snoc
71 | , merge
72 | , mergeBy
73 | , sort
74 | , sortBy
75 | , sortOn
76 | , span
77 | , stripPrefix
78 | , stripSuffix
79 | , tailOpt
80 | , take
81 | , takeWhile
82 | , transpose
83 | , unzip
84 | , unzipL
85 | , unzipLL
86 | , zip
87 | , zipLL
88 | , zipLS
89 | , zipSL
90 | , zipWith
91 | , zipWithLS
92 | , zipWithSL
93 | )
94 | where
95 |
96 | import Data.Option hiding (catOptions, mapOption)
97 | import Data.StrictList.Types
98 | import Data.StrictTuple
99 |
100 | import Data.Hashable
101 | import Data.Ord (comparing)
102 | import Prelude hiding
103 | ( (!!)
104 | , all
105 | , any
106 | , break
107 | , concat
108 | , concatMap
109 | , drop
110 | , dropWhile
111 | , elem
112 | , filter
113 | , length
114 | , lookup
115 | , map
116 | , mapM
117 | , mapM_
118 | , notElem
119 | , null
120 | , replicate
121 | , reverse
122 | , span
123 | , take
124 | , takeWhile
125 | , unzip
126 | , zip
127 | , zipWith
128 | )
129 | import Safe.Plus
130 | import qualified Data.Foldable as F
131 | import qualified Data.HashSet as HashSet
132 | import qualified Data.List as L
133 | import qualified Data.Text as T
134 | import qualified Data.Traversable as Tr
135 | import qualified Prelude as P
136 |
137 | sl :: [a] -> SL a
138 | sl = fromLazyList
139 |
140 | ll :: SL a -> [a]
141 | ll = toLazyList
142 |
143 | -- |
144 | -- >>> null (sl [])
145 | -- True
146 | --
147 | -- >>> null (sl ["foo"])
148 | -- False
149 | null :: StrictList a -> Bool
150 | null Nil = True
151 | null _ = False
152 |
153 | -- |
154 | -- prop> not (null xs) ==> isSome (headOpt xs)
155 | headOpt :: StrictList a -> Option a
156 | headOpt Nil = None
157 | headOpt (x :! _) = Some x
158 |
159 | headM :: Monad m => StrictList a -> m a
160 | headM xxs =
161 | case xxs of
162 | Nil -> safeFail "headM of empty strict list."
163 | (x :! _) -> return x
164 |
165 | -- | Safe 'Prelude.tail' function: Returns 'None' for an empty list,
166 | -- 'Some' @x@ for a non-empty list starting with @x@.
167 | tailOpt :: StrictList a -> Option (StrictList a)
168 | tailOpt Nil = None
169 | tailOpt (_ :! xs) = Some xs
170 |
171 | lastOpt :: StrictList a -> Option a
172 | lastOpt = lastM
173 |
174 | lastM :: Monad m => StrictList a -> m a
175 | lastM xxs =
176 | case xxs of
177 | Nil -> safeFail "No last element in strict list."
178 | (x :! Nil) -> return x
179 | (_ :! xs) -> lastM xs
180 |
181 | -- |
182 | -- >>> optionToStrictList (Some "foo")
183 | -- ["foo"]
184 | --
185 | -- >>> optionToStrictList None
186 | -- []
187 | optionToStrictList :: Option a -> StrictList a
188 | optionToStrictList None = Nil
189 | optionToStrictList (Some x) = x :! Nil
190 |
191 | -- |
192 | -- >>> maybeToStrictList (Just "bar")
193 | -- ["bar"]
194 | --
195 | -- >>> maybeToStrictList Nothing
196 | -- []
197 | maybeToStrictList :: Maybe a -> StrictList a
198 | maybeToStrictList Nothing = Nil
199 | maybeToStrictList (Just x) = x :! Nil
200 |
201 | takeWhile :: (a -> Bool) -> StrictList a -> StrictList a
202 | takeWhile _ Nil = Nil
203 | takeWhile p (x :! xs)
204 | | p x = x :! takeWhile p xs
205 | | otherwise = Nil
206 |
207 | -- |
208 | -- >>> drop 3 (sl [1, 2, 3, 4, 5])
209 | -- [4,5]
210 | drop :: Int -> StrictList a -> StrictList a
211 | drop _ Nil = Nil
212 | drop n xss@(_ :! xs)
213 | | n <= 0 = xss
214 | | otherwise = drop (n - 1) xs
215 |
216 | -- |
217 | -- 'deleteIdx' @idx@ removes the element at index @idx@.
218 | --
219 | -- prop> not (null xs) ==> Some (deleteIdx 0 xs) == tailOpt xs
220 | deleteIdx :: Int -> StrictList a -> StrictList a
221 | deleteIdx _ Nil = Nil
222 | deleteIdx idx lst@(x :! xs) =
223 | case idx of
224 | 0 ->
225 | case xs of
226 | Nil -> Nil
227 | l -> l
228 | i ->
229 | if i < 0
230 | then lst
231 | else x :! deleteIdx (i-1) xs
232 |
233 | -- | 'delete' @x@ removes the first occurrence of @x@ from its list argument.
234 | -- NOTE: Implementation copied from Data.List.
235 | delete :: (Eq a) => a -> SL a -> SL a
236 | delete = deleteBy (==)
237 |
238 | -- | The 'deleteBy' function behaves like 'delete', but takes a
239 | -- user-supplied equality predicate.
240 | -- NOTE: Implementation copied from Data.List.
241 | deleteBy :: (a -> a -> Bool) -> a -> SL a -> SL a
242 | deleteBy eq x yys =
243 | case yys of
244 | Nil -> Nil
245 | (y:!ys) -> if x `eq` y then ys else y :! deleteBy eq x ys
246 |
247 | atIdx :: Int -> StrictList a -> Option a
248 | atIdx _ Nil = None
249 | atIdx idx (p :! ps) =
250 | case idx of
251 | 0 -> Some p
252 | i ->
253 | if i < 0
254 | then None
255 | else atIdx (i-1) ps
256 |
257 | dropWhile :: (a -> Bool) -> StrictList a -> StrictList a
258 | dropWhile _ Nil = Nil
259 | dropWhile p (x :! xs)
260 | | p x = dropWhile p xs
261 | | otherwise = x :! xs
262 |
263 | findIndex :: (a -> Bool) -> StrictList a -> Option Int
264 | findIndex _ Nil = None
265 | findIndex p (x :! xs)
266 | | p x = Some 0
267 | | otherwise = (+1) <$> findIndex p xs
268 |
269 | map :: (a -> b) -> StrictList a -> StrictList b
270 | map = fmap
271 |
272 | mapM :: Monad m => (a -> m b) -> StrictList a -> m (StrictList b)
273 | mapM = Tr.mapM
274 |
275 | mapM_ :: Monad m => (a -> m b) -> StrictList a -> m ()
276 | mapM_ = F.mapM_
277 |
278 | -- | Equivalent of 'Prelude.filter' with 'StrictList'.
279 | filter :: (a -> Bool) -> StrictList a -> StrictList a
280 | filter _ Nil = Nil
281 | filter pred (x :! xs)
282 | | pred x = x :! filter pred xs
283 | | otherwise = filter pred xs
284 |
285 | -- | Equivalent of 'Data.Maybe.catMaybes' with 'StrictList'.
286 | catMaybes :: StrictList (Maybe a) -> StrictList a
287 | catMaybes xs =
288 | case xs of
289 | Nil -> Nil
290 | (Nothing :! xs) -> catMaybes xs
291 | (Just x :! xs ) -> x :! catMaybes xs
292 |
293 | -- | Equivalent of 'Data.Maybe.mapMaybe' with 'StrictList'.
294 | mapMaybe :: (a -> Maybe b) -> StrictList a -> StrictList b
295 | mapMaybe f = catMaybes . map f
296 |
297 | -- | Equivalent of 'Data.Maybe.mapMaybe' with 'Option' and 'StrictList'.
298 | --
299 | -- >>> mapOption (\x -> if even x then Some (x * 2) else None) (sl [1, 2, 3, 4, 5])
300 | -- [4,8]
301 | mapOption :: (a -> Option b) -> StrictList a -> StrictList b
302 | mapOption f = catOptions . map f
303 |
304 | -- | Equivalent to 'Data.Maybe.catMaybes' with 'Option' and 'StrictList'.
305 | --
306 | -- >>> catOptions (sl [Some 1, None, Some 2, None, None, Some 3, Some 4])
307 | -- [1,2,3,4]
308 | catOptions :: StrictList (Option a) -> StrictList a
309 | catOptions xs =
310 | case xs of
311 | Nil -> Nil
312 | (None :! xs) -> catOptions xs
313 | (Some x :! xs) -> x :! catOptions xs
314 |
315 | -- |
316 | -- >>> catOptionsL [Some 1, None, Some 2, None, None, Some 3, Some 4]
317 | -- [1,2,3,4]
318 | catOptionsL :: [Option a] -> StrictList a
319 | catOptionsL xs =
320 | case xs of
321 | [] -> Nil
322 | (None : xs) -> catOptionsL xs
323 | (Some x : xs) -> x :! catOptionsL xs
324 |
325 | -- |
326 | -- >>> take 3 (sl [1, 2, 3, 4, 5, 6, 7])
327 | -- [1,2,3]
328 | take :: Int -> StrictList a -> StrictList a
329 | take _ Nil = Nil
330 | take n _ | n <= 0 = Nil
331 | take n (x :! xs) = x :! take (n-1) xs
332 |
333 | sort :: (Ord a) => StrictList a -> StrictList a
334 | sort = sortBy compare
335 |
336 | -- |
337 | -- >>> sortOn snd (sl [("foo", 10), ("bar", 1), ("baz", 100)])
338 | -- [("bar",1),("foo",10),("baz",100)]
339 | sortOn :: (Ord b) => (a -> b) -> StrictList a -> StrictList a
340 | sortOn f =
341 | map snd
342 | . sortBy (comparing fst)
343 | . map (\x -> let y = f x
344 | in y `seq` (y,x))
345 |
346 | replicate :: Integral i => i -> a -> StrictList a
347 | replicate i a =
348 | case i of
349 | 0 -> Nil
350 | n -> a :! replicate (n-1) a
351 |
352 | -- |
353 | -- prop> reverse (reverse xs) == xs
354 | reverse :: StrictList a -> StrictList a
355 | reverse l = rev l Nil
356 | where
357 | rev xxs !a =
358 | case xxs of
359 | Nil -> a
360 | (x :! xs) -> rev xs (x :! a)
361 |
362 | merge :: Ord a => StrictList a -> StrictList a -> StrictList a
363 | merge = mergeBy compare
364 |
365 | mergeBy :: (a -> a -> Ordering) -> StrictList a -> StrictList a -> StrictList a
366 | mergeBy cmp = go
367 | where
368 | go as@(a :! as') bs@(b :! bs') =
369 | case cmp a b of
370 | LT -> a :! go as' bs
371 | GT -> b :! go as bs'
372 | EQ -> a :! go as' bs'
373 | go Nil bs = bs
374 | go as Nil = as
375 |
376 | sortBy :: (a -> a -> Ordering) -> StrictList a -> StrictList a
377 | sortBy cmp = mergeAll . sequences
378 | where
379 | sequences (a :! (b :! xs))
380 | | a `cmp` b == GT = descending b (a :! Nil) xs
381 | | otherwise = ascending b (a :!) xs
382 | sequences xs = xs :! Nil
383 | descending a as (b :! bs)
384 | | a `cmp` b == GT = descending b (a :! as) bs
385 | descending a as bs = (a :! as) :! sequences bs
386 | ascending a as (b:!bs)
387 | | a `cmp` b /= GT = ascending b (\ys -> as (a :! ys)) bs
388 | ascending a as bs = as (a :! Nil) :! sequences bs
389 | mergeAll (x :! Nil) = x
390 | mergeAll xs = mergeAll (mergePairs xs)
391 | mergePairs (a :! (b :! xs)) = (merge a b) :! mergePairs xs
392 | mergePairs xs = xs
393 | merge as@(a :! as') bs@(b :! bs')
394 | | a `cmp` b == GT = b :! merge as bs'
395 | | otherwise = a :! merge as' bs
396 | merge Nil bs = bs
397 | merge as Nil = as
398 |
399 | span :: (a -> Bool) -> StrictList a -> (StrictList a, StrictList a)
400 | span _ Nil = (Nil, Nil)
401 | span p xs@(x :! xs')
402 | | p x = let (ys, zs) = span p xs' in (x :! ys, zs)
403 | | otherwise = (Nil, xs)
404 |
405 | break :: (a -> Bool) -> StrictList a -> (StrictList a, StrictList a)
406 | break p = span (not . p)
407 |
408 | concat :: F.Foldable t => t (StrictList a) -> StrictList a
409 | concat = F.fold
410 |
411 | concatSL :: SL (SL a) -> SL a
412 | concatSL = concat
413 |
414 | concatMap :: F.Foldable t => (a -> StrictList b) -> t a -> StrictList b
415 | concatMap = F.foldMap
416 |
417 | concatMapSL :: (a -> StrictList b) -> SL a -> StrictList b
418 | concatMapSL = concatMap
419 |
420 | concatMapM :: (Monad m) => (a -> m (SL b)) -> SL a -> m (SL b)
421 | concatMapM f xs = concat <$> mapM f xs
422 |
423 | any :: (a -> Bool) -> StrictList a -> Bool
424 | any = F.any
425 |
426 | all :: (a -> Bool) -> StrictList a -> Bool
427 | all = F.all
428 |
429 | elem :: Eq a => a -> StrictList a -> Bool
430 | elem = F.elem
431 |
432 | notElem :: Eq a => a -> StrictList a -> Bool
433 | notElem = F.notElem
434 |
435 | find :: (a -> Bool) -> StrictList a -> Maybe a
436 | find = F.find
437 |
438 | zip :: StrictList a -> StrictList b -> StrictList (a :!: b)
439 | zip Nil _ = Nil
440 | zip _ Nil = Nil
441 | zip (x :! xs) (y :! ys) = (x :!: y) :! (zip xs ys)
442 |
443 | zipSL :: StrictList a -> [b] -> StrictList (a :!: b)
444 | zipSL Nil _ = Nil
445 | zipSL _ [] = Nil
446 | zipSL (x :! xs) (y : ys) = (x :!: y) :! (zipSL xs ys)
447 |
448 | zipLS :: [a] -> StrictList b -> StrictList (a :!: b)
449 | zipLS [] _ = Nil
450 | zipLS _ Nil = Nil
451 | zipLS (x : xs) (y :! ys) = (x :!: y) :! (zipLS xs ys)
452 |
453 | zipLL :: [a] -> [b] -> StrictList (a :!: b)
454 | zipLL [] _ = Nil
455 | zipLL _ [] = Nil
456 | zipLL (x : xs) (y : ys) = (x :!: y) :! (zipLL xs ys)
457 |
458 | zipWith :: (a->b->c) -> SL a-> SL b -> SL c
459 | zipWith f (a:!as) (b:!bs) = f a b :! zipWith f as bs
460 | zipWith _ _ _ = Nil
461 |
462 | -- zipWith - left list is lazy, right list is strict
463 | zipWithLS :: (a->b->c) -> [a]-> SL b -> SL c
464 | zipWithLS f (a:as) (b:!bs) = f a b :! zipWithLS f as bs
465 | zipWithLS _ _ _ = Nil
466 |
467 | -- zipWith - left list is strict, right list is lazy
468 | zipWithSL :: (a->b->c) -> SL a-> [b] -> SL c
469 | zipWithSL f (a:!as) (b:bs) = f a b :! zipWithSL f as bs
470 | zipWithSL _ _ _ = Nil
471 |
472 | concatText :: StrictList T.Text -> T.Text
473 | concatText = T.concat . toLazyList
474 |
475 | concatString :: StrictList String -> String
476 | concatString = P.concat . toLazyList
477 |
478 | groupBy :: (a -> a -> Bool) -> StrictList a -> StrictList (StrictList a)
479 | groupBy _ Nil = Nil
480 | groupBy eq (x:!xs) = (x:!ys) :! groupBy eq zs
481 | where (ys,zs) = span (eq x) xs
482 |
483 | intersperse :: a -> StrictList a -> StrictList a
484 | intersperse y =
485 | F.foldr' prepend Nil
486 | where
487 | prepend x xs =
488 | case xs of
489 | Nil -> x :! Nil
490 | _ -> x :! y :! xs
491 |
492 | intercalateText :: T.Text -> StrictList T.Text -> T.Text
493 | intercalateText t =
494 | concatText . intersperse t
495 |
496 | intercalateString :: String -> SL String -> String
497 | intercalateString s =
498 | concatString . intersperse s
499 |
500 | singleton :: a -> StrictList a
501 | singleton x =
502 | x :! Nil
503 |
504 | lookupM' :: (Monad m, Eq a) => (a -> String) -> a -> StrictList (a :!: b) -> m b
505 | lookupM' showA x = fmap snd' . lookupM'' showA (Just . fst') x
506 |
507 | -- | @lookupM'' showKey getKey getValue key list@ searches for @key@ in
508 | -- @list@ using @getKey@ as the key extraction function and @showKey@ to print
509 | -- all available keys when no match is found.
510 | lookupM'' :: (Monad m, Eq k) => (k -> String) -> (a -> Maybe k) -> k -> StrictList a -> m a
511 | lookupM'' showKey getKey wantedK list = loop list
512 | where
513 | loop xxs =
514 | case xxs of
515 | Nil ->
516 | let keys = ll $ mapMaybe getKey list
517 | keyCount = P.length keys
518 | count = P.length list
519 | in safeFail $
520 | "Didn't find " ++ showKey wantedK ++ " in the list with these keys ["
521 | ++ L.intercalate ", " (fmap showKey keys) ++ "]. " ++
522 | if keyCount == count
523 | then ""
524 | else ("Only " ++ show keyCount ++ "/" ++ show count ++ " entries had a key.")
525 | (x@(getKey -> Just curK) :! xs)
526 | | wantedK == curK -> return x
527 | | otherwise -> loop xs
528 | _ :! xs -> loop xs
529 |
530 | lookupM :: (Monad m, Show a, Eq a) => a -> StrictList (a :!: b) -> m b
531 | lookupM = lookupM' show
532 |
533 | lookup :: Eq a => a -> StrictList (a :!: b) -> Option b
534 | lookup = lookupM' (const "fail in Option is None")
535 |
536 | insert :: Ord a => a -> SL a -> SL a
537 | insert = insertBy compare
538 |
539 | insertBy :: (a -> a -> Ordering) -> a -> SL a -> SL a
540 | insertBy cmp x yss =
541 | case yss of
542 | Nil -> x :! Nil
543 | y:!ys ->
544 | case cmp x y of
545 | GT -> y :! insertBy cmp x ys
546 | _ -> x :! yss
547 |
548 | partition :: (a -> Bool) -> SL a -> (SL a, SL a)
549 | partition p =
550 | F.foldr (select p) (Nil, Nil)
551 | where
552 | select :: (a -> Bool) -> a -> (SL a, SL a) -> (SL a, SL a)
553 | select p x (ts, fs)
554 | | p x = (x :! ts, fs)
555 | | otherwise = (ts, x :! fs)
556 |
557 | dropWhileEnd :: (a -> Bool) -> SL a -> SL a
558 | dropWhileEnd p =
559 | F.foldr (\x xs -> if p x && null xs then Nil else x :! xs) Nil
560 |
561 | maximumM :: (Ord a, Monad m) => SL a -> m a
562 | maximumM xxs =
563 | case xxs of
564 | Nil -> safeFail "Empty list doesn't have a maximum."
565 | (x :! xs) -> return $! loop x xs
566 | where
567 | loop x yys =
568 | case yys of
569 | Nil -> x
570 | (y :! ys) -> loop (max x y) ys
571 |
572 | mconcatSL :: Monoid a => SL a -> a
573 | mconcatSL = F.foldr mappend mempty
574 |
575 | stripPrefix :: Eq a => SL a -> SL a -> Maybe (SL a)
576 | stripPrefix Nil ys = Just ys
577 | stripPrefix (x :! xs) (y :! ys) | x == y = stripPrefix xs ys
578 | stripPrefix _ _ = Nothing
579 |
580 | stripSuffix :: Eq a => SL a -> SL a -> Maybe (SL a)
581 | stripSuffix suffix xs = fmap reverse (stripPrefix (reverse suffix) (reverse xs))
582 |
583 | -- unzip strict list of strict tuples to strict lists of strict tuples
584 | unzip :: SL (a :!: b) -> (SL a :!: SL b)
585 | unzip = F.foldr (\(a :!: b) (as :!: bs) -> (a:!as :!: b:!bs)) (Nil :!: Nil)
586 |
587 | -- unzip lazy list of lazy tuples to strict lists of strict tuples
588 | unzipLL :: [(a,b)] -> (SL a :!: SL b)
589 | unzipLL = F.foldr (\(a,b) (as :!: bs) -> (a:!as :!: b:!bs)) (Nil :!: Nil)
590 |
591 | -- unzip lazy list of strict tuples to strict lists of strict tuples
592 | unzipL :: [(a:!:b)] -> (SL a :!: SL b)
593 | unzipL = F.foldr (\(a:!:b) (as :!: bs) -> (a:!as :!: b:!bs)) (Nil :!: Nil)
594 |
595 | -- | Appends an element to the end of this list. This is really inefficient because the
596 | -- whole list needs to be copied. Use at your own risk.
597 | snoc :: SL a -> a -> SL a
598 | snoc xxs y =
599 | case xxs of
600 | Nil -> y :! Nil
601 | x :! xs -> x :! snoc xs y
602 |
603 | -- NOTE: copied from Data.List
604 | transpose :: SL (SL a) -> SL (SL a)
605 | transpose xxs =
606 | case xxs of
607 | Nil -> Nil
608 | (Nil :! ys) -> transpose ys
609 | ((x:!xs) :! xss) -> (x :! [h | (h:!_) <- xss]) :! transpose (xs :! [ t | (_:!t) <- xss])
610 |
611 | (\!\) :: (Eq a) => SL a -> SL a -> SL a
612 | (\!\) = F.foldl (flip delete)
613 |
614 | nub :: (Eq a, Hashable a) => SL a -> SL a
615 | nub = nub' HashSet.empty
616 | where
617 | nub' acc xxs =
618 | case xxs of
619 | Nil -> Nil
620 | x :! xs
621 | | x `HashSet.member` acc -> nub' acc xs
622 | | otherwise -> x :! nub' (HashSet.insert x acc) xs
623 |
--------------------------------------------------------------------------------
/strict-data/src/Data/StrictList/Types.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveDataTypeable #-}
2 | {-# LANGUAGE DeriveFoldable #-}
3 | {-# LANGUAGE DeriveFunctor #-}
4 | {-# LANGUAGE DeriveGeneric #-}
5 | {-# LANGUAGE DeriveTraversable #-}
6 | {-# LANGUAGE MonadComprehensions #-}
7 | {-# LANGUAGE ScopedTypeVariables #-}
8 | {-# LANGUAGE TypeFamilies #-}
9 | module Data.StrictList.Types where
10 |
11 | import Control.Applicative
12 | import Control.DeepSeq
13 | import Control.Monad hiding (forM_, mapM, mapM_)
14 | import Data.Aeson
15 | import Data.Data
16 | import Data.Hashable (Hashable)
17 | import Data.Monoid
18 | import Data.Traversable hiding (mapM)
19 | import GHC.Exts
20 | import GHC.Generics (Generic)
21 | import Prelude (Eq(..), Ord(..), Show(..), (.), (+), Bool)
22 | import Test.QuickCheck
23 | import Text.Read
24 | import qualified Control.Monad.Fail
25 | import qualified Data.Foldable as F
26 |
27 | type SL = StrictList
28 |
29 | data StrictList a
30 | = Nil
31 | | !a :! !(StrictList a)
32 | deriving (Eq,Ord,Functor,F.Foldable,Traversable,Typeable,Generic,Data)
33 |
34 | instance Read a => Read (StrictList a) where
35 | readPrec = fromLazyList <$> readPrec
36 |
37 | instance Show a => Show (StrictList a) where
38 | showsPrec n xs = showsPrec n (toLazyList xs)
39 |
40 | infixr 5 +!+
41 | infixr 5 :!
42 |
43 | (+!+) :: StrictList a -> StrictList a -> StrictList a
44 | (+!+) Nil ys = ys
45 | (+!+) (x :! xs) ys = x :! (xs +!+ ys)
46 |
47 | instance Applicative StrictList where
48 | pure = return
49 | (<*>) = ap
50 |
51 | instance Alternative StrictList where
52 | empty = Nil
53 | (<|>) = (+!+)
54 |
55 | instance Control.Monad.Fail.MonadFail StrictList where
56 | fail _ = Nil
57 |
58 | instance Monad StrictList where
59 | return = (:! Nil)
60 | (>>=) xs f = F.asum (fmap f xs)
61 | fail = Control.Monad.Fail.fail
62 |
63 | instance Arbitrary a => Arbitrary (StrictList a) where
64 | arbitrary =
65 | do v <- arbitrary
66 | return (fromLazyList v)
67 |
68 | instance Monoid (StrictList a) where
69 | mempty = Nil
70 | mappend = (+!+)
71 |
72 | instance Hashable a => Hashable (StrictList a)
73 |
74 | instance ToJSON a => ToJSON (StrictList a) where
75 | toJSON = toJSON . toLazyList
76 |
77 | instance FromJSON a => FromJSON (StrictList a) where
78 | parseJSON = fmap fromLazyList . parseJSON
79 |
80 | instance NFData a => NFData (StrictList a)
81 |
82 | instance IsList (StrictList a) where
83 | type Item (StrictList a) = a
84 | fromList = fromLazyList
85 | toList = toLazyList
86 |
87 | length :: StrictList a -> Int
88 | length xxs =
89 | case xxs of
90 | _ :! xs -> 1 + length xs
91 | Nil -> 0
92 |
93 | fromLazyList :: [a] -> StrictList a
94 | fromLazyList [] = Nil
95 | fromLazyList (x : xs) = x :! fromLazyList xs
96 |
97 | toLazyList :: StrictList a -> [a]
98 | toLazyList Nil = []
99 | toLazyList (x :! xs) = x : toLazyList xs
100 |
101 | prop_StrictListOrd :: [Int] -> [Int] -> Bool
102 | prop_StrictListOrd l1 l2 =
103 | let l1' = fromLazyList l1
104 | l2' = fromLazyList l2
105 | in compare l1 l2 == compare l1' l2'
106 |
--------------------------------------------------------------------------------
/strict-data/src/Data/StrictTuple.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-orphans #-}
2 | {-# LANGUAGE DeriveDataTypeable #-}
3 | {-# LANGUAGE StandaloneDeriving #-}
4 | {-# LANGUAGE TypeOperators #-}
5 | module Data.StrictTuple
6 | ( module Data.Strict.Tuple
7 | , toLazyTuple
8 | , fromLazyTuple
9 | , fst', snd'
10 | , uncurry'
11 | , first, second
12 | , swap, swap'
13 | , fst3, snd3, thr3
14 | , fst3', snd3', thr3'
15 | )
16 | where
17 |
18 | import Control.DeepSeq (NFData(..))
19 | import Data.Aeson
20 | import Data.Data
21 | import Data.Hashable
22 | import Data.Strict.Tuple hiding (fst, snd)
23 | import Data.Tuple
24 | import Test.QuickCheck
25 | import qualified Data.Strict.Tuple
26 |
27 | deriving instance Typeable Pair
28 | deriving instance (Data a, Data b) => Data (Pair a b)
29 |
30 | instance (Hashable a, Hashable b) => Hashable (Pair a b) where
31 | hashWithSalt s (a :!: b) = hashWithSalt s a `hashWithSalt` b
32 |
33 | instance (NFData a, NFData b) => NFData (Pair a b) where
34 | rnf (a :!: b) = rnf a `seq` rnf b
35 |
36 | instance (Monoid a, Monoid b) => Monoid (Pair a b) where
37 | mempty = mempty :!: mempty
38 | (a1 :!: b1) `mappend` (a2 :!: b2) = a1 `mappend` a2 :!: b1 `mappend` b2
39 |
40 | instance (Arbitrary a, Arbitrary b) => Arbitrary (Pair a b) where
41 | arbitrary = (:!:) <$> arbitrary <*> arbitrary
42 |
43 | instance (ToJSON a, ToJSON b) => ToJSON (Pair a b) where
44 | toJSON = toJSON . toLazyTuple
45 |
46 | instance (FromJSON a, FromJSON b) => FromJSON (Pair a b) where
47 | parseJSON = fmap fromLazyTuple . parseJSON
48 |
49 | toLazyTuple :: a :!: b -> (a, b)
50 | toLazyTuple (x :!: y) = (x, y)
51 |
52 | fromLazyTuple :: (a, b) -> a :!: b
53 | fromLazyTuple (x, y) = x :!: y
54 |
55 | fst' :: Pair a b -> a
56 | fst' = Data.Strict.Tuple.fst
57 |
58 | snd' :: Pair a b -> b
59 | snd' = Data.Strict.Tuple.snd
60 |
61 | uncurry' :: (a -> b -> c) -> Pair a b -> c
62 | uncurry' = Data.Strict.Tuple.uncurry
63 |
64 | first :: (a -> b) -> (a :!: c) -> (b :!: c)
65 | first f (a :!: c) = f a :!: c
66 |
67 | second :: (b -> c) -> (a :!: b) -> (a :!: c)
68 | second f (a :!: b) = a :!: f b
69 |
70 | swap' :: (a :!: b) -> (b :!: a)
71 | swap' (x :!: y) = y :!: x
72 |
73 | fst3 :: (a, b, c) -> a
74 | fst3 (x, _, _) = x
75 |
76 | snd3 :: (a, b, c) -> b
77 | snd3 (_, x, _) = x
78 |
79 | thr3 :: (a, b, c) -> c
80 | thr3 (_, _, x) = x
81 |
82 | fst3' :: (a :!: b :!: c) -> a
83 | fst3' (x :!: _ :!: _) = x
84 |
85 | snd3' :: (a :!: b :!: c) -> b
86 | snd3' (_ :!: x :!: _) = x
87 |
88 | thr3' :: (a :!: b :!: c) -> c
89 | thr3' (_ :!: _ :!: x) = x
90 |
--------------------------------------------------------------------------------
/strict-data/src/Data/StrictVector.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2 | {-# LANGUAGE BangPatterns #-}
3 | {-# LANGUAGE DeriveDataTypeable #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | {-# LANGUAGE FlexibleInstances #-}
6 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
7 | {-# LANGUAGE MultiParamTypeClasses #-}
8 | {-# LANGUAGE TypeFamilies #-}
9 | module Data.StrictVector
10 | ( module Data.Vector.Generic
11 | , Vector
12 | , null, length, (!), (!?), head, last
13 | , fromList, fromListN, toList, empty, singleton
14 | , generate, generateM
15 | , catMaybes, mapMaybe, lastMay, toHashSet
16 | , lookAround
17 | , dropWhileEnd
18 | , dropWhileLookingAround
19 | , fromSL
20 | , imapM, binarySearchL, binarySearchR
21 | , sort
22 | , sortBy
23 | , sortOn
24 | , groupBy
25 | , groupOn
26 | , toSL
27 | , uncons
28 | , updateVector, updateVectorWith
29 | , unfoldrM, unfoldrNM
30 | , theOnlyOne
31 | ) where
32 |
33 | import Data.Option
34 | import Data.StrictList (SL, toLazyList, fromLazyList)
35 | -- import qualified Cpm.Util.List as L
36 | import qualified Data.StrictVector.Mutable as VM
37 |
38 | import Control.DeepSeq (NFData)
39 | import Control.Monad
40 | import Data.Aeson (ToJSON, FromJSON(..))
41 | import Data.Bits (shiftR)
42 | import Data.Data
43 | import Data.HashSet (HashSet)
44 | import Data.Hashable (Hashable(..))
45 | import Data.Vector.Generic hiding
46 | ( Vector, fromList, fromListN, toList, empty, singleton, null, length ,(!), (!?), head, last
47 | , imapM, generate, generateM, unfoldrNM, unfoldrM, mapMaybe)
48 | import Prelude hiding
49 | ( map, drop, dropWhile, concatMap, length, zip3, mapM, null, (++), replicate, head, last)
50 | import Safe.Plus
51 | import Test.QuickCheck (Arbitrary(..))
52 | import Text.Read
53 | import qualified Control.Applicative as A
54 | import qualified Control.Monad.Fail
55 | import qualified Data.HashSet as HashSet
56 | import qualified Data.Vector as V
57 | import qualified Data.Vector.Algorithms.Intro as VA (sortBy, sort)
58 | import qualified Data.Vector.Fusion.Bundle.Monadic as VFM
59 | import qualified Data.Vector.Generic as VG
60 | import qualified GHC.Exts as Exts
61 |
62 | newtype Vector a = Vector (V.Vector a)
63 | deriving (Eq, Ord, NFData, ToJSON, Monoid, Foldable, Data, Typeable)
64 |
65 | type instance VG.Mutable Vector = VM.MVector
66 |
67 | instance VG.Vector Vector a where
68 | basicUnsafeFreeze (VM.MVector v) = fmap Vector (basicUnsafeFreeze v)
69 | basicUnsafeThaw (Vector v) = fmap VM.MVector (basicUnsafeThaw v)
70 | basicLength (Vector v) = basicLength v
71 | basicUnsafeSlice n m (Vector v) = Vector (basicUnsafeSlice n m v)
72 | basicUnsafeIndexM (Vector v) = basicUnsafeIndexM v
73 | basicUnsafeCopy (VM.MVector v1) (Vector v2) = basicUnsafeCopy v1 v2
74 | elemseq _ = seq
75 |
76 | instance Show a => Show (Vector a) where
77 | showsPrec = VG.showsPrec
78 |
79 | instance Read a => Read (Vector a) where
80 | readPrec = VG.readPrec
81 | readListPrec = readListPrecDefault
82 |
83 | instance (Hashable a) => Hashable (Vector a) where
84 | hashWithSalt = hashVectorWithSalt
85 |
86 | instance Arbitrary a => Arbitrary (Vector a) where
87 | arbitrary = VG.fromList <$> arbitrary
88 |
89 | instance Functor Vector where
90 | {-# INLINE fmap #-}
91 | fmap = VG.map
92 |
93 | instance Control.Monad.Fail.MonadFail Vector where
94 | {-# INLINE fail #-}
95 | fail _ = VG.empty
96 |
97 | instance Monad Vector where
98 | {-# INLINE return #-}
99 | return = VG.singleton
100 | {-# INLINE (>>=) #-}
101 | (>>=) = flip VG.concatMap
102 | {-# INLINE fail #-}
103 | fail = safeFail
104 |
105 | instance MonadPlus Vector where
106 | {-# INLINE mzero #-}
107 | mzero = VG.empty
108 | {-# INLINE mplus #-}
109 | mplus = (VG.++)
110 |
111 | instance Applicative Vector where
112 | {-# INLINE pure #-}
113 | pure = VG.singleton
114 | {-# INLINE (<*>) #-}
115 | (<*>) = ap
116 |
117 | instance A.Alternative Vector where
118 | {-# INLINE empty #-}
119 | empty = VG.empty
120 | {-# INLINE (<|>) #-}
121 | (<|>) = (VG.++)
122 |
123 | instance Traversable Vector where
124 | {-# INLINE traverse #-}
125 | traverse f xs = fromList <$> traverse f (toList xs)
126 | {-# INLINE mapM #-}
127 | mapM = VG.mapM
128 | {-# INLINE sequence #-}
129 | sequence = VG.sequence
130 |
131 | instance Exts.IsList (Vector a) where
132 | type Item (Vector a) = a
133 | fromList = fromList
134 | fromListN = fromListN
135 | toList = toList
136 |
137 | instance FromJSON a => FromJSON (Vector a) where
138 | parseJSON x = (convert :: V.Vector a -> Vector a) <$> parseJSON x
139 |
140 | -- | /O(1)/ Yield the length of the vector.
141 | length :: Vector a -> Int
142 | {-# INLINE length #-}
143 | length = VG.length
144 |
145 | -- | /O(1)/ Test whether a vector if empty
146 | null :: Vector a -> Bool
147 | {-# INLINE null #-}
148 | null = VG.null
149 |
150 | -- | O(1) Indexing
151 | (!) :: Vector a -> Int -> a
152 | {-# INLINE (!) #-}
153 | (!) = (VG.!)
154 |
155 | -- | O(1) Safe indexing
156 | (!?) :: Vector a -> Int -> Maybe a
157 | {-# INLINE (!?) #-}
158 | (!?) = (VG.!?)
159 |
160 | -- | /O(1)/ First element
161 | head :: Vector a -> a
162 | {-# INLINE head #-}
163 | head = VG.head
164 |
165 | -- | /O(1)/ Last element
166 | last :: Vector a -> a
167 | {-# INLINE last #-}
168 | last = VG.last
169 |
170 | fromList :: [a] -> Vector a
171 | {-# INLINE fromList #-}
172 | fromList = VG.fromList
173 |
174 | fromListN :: Int -> [a] -> Vector a
175 | {-# INLINE fromListN #-}
176 | fromListN = VG.fromListN
177 |
178 | toList :: Vector a -> [a]
179 | {-# INLINE toList #-}
180 | toList = VG.toList
181 |
182 | singleton :: a -> Vector a
183 | {-# INLINE singleton #-}
184 | singleton = VG.singleton
185 |
186 | empty :: Vector a
187 | {-# INLINE empty #-}
188 | empty = VG.empty
189 |
190 | generate :: Int -> (Int -> a) -> Vector a
191 | {-# INLINE generate #-}
192 | generate = VG.generate
193 |
194 | generateM :: Monad m => Int -> (Int -> m a) -> m (Vector a)
195 | {-# INLINE generateM #-}
196 | generateM = VG.generateM
197 |
198 | fromSL :: SL a -> Vector a
199 | {-# INLINE fromSL #-}
200 | fromSL = VG.fromList . toLazyList
201 |
202 | toSL :: Vector a -> SL a
203 | {-# INLINE toSL #-}
204 | toSL = fromLazyList . toList
205 |
206 | {-# INLINABLE hashVectorWithSalt #-}
207 | hashVectorWithSalt :: Hashable a => Int -> Vector a -> Int
208 | hashVectorWithSalt salt v = foldl' hashWithSalt salt v
209 |
210 | {-# INLINABLE mapMaybe #-}
211 | mapMaybe :: (a -> Maybe b) -> Vector a -> Vector b
212 | mapMaybe f = catMaybes . map f
213 |
214 | {-# INLINABLE catMaybes #-}
215 | catMaybes :: Vector (Maybe a) -> Vector a
216 | catMaybes = concatMap maybeToVector
217 |
218 | {-# INLINABLE maybeToVector #-}
219 | maybeToVector :: Maybe a -> Vector a
220 | maybeToVector Nothing = VG.empty
221 | maybeToVector (Just x) = VG.singleton x
222 |
223 | {-# INLINABLE lastMay #-}
224 | lastMay :: Vector a -> Maybe a
225 | lastMay vec =
226 | vec !? ((length vec) - 1)
227 |
228 | uncons :: Vector a -> Option (a, Vector a)
229 | uncons v | null v = None
230 | | otherwise = Some (unsafeHead v, drop 1 v)
231 |
232 | -- | Returns `Just` the only element of the vector if there is exactly
233 | -- one element or `Nothing` otherwise.
234 | theOnlyOne :: Vector a -> Maybe a
235 | theOnlyOne xs
236 | | length xs /= 1 = Nothing
237 | | otherwise = xs !? 0
238 |
239 | {-# INLINABLE lookAround #-}
240 | lookAround :: Vector a -> Vector (Maybe a, a, Maybe a)
241 | lookAround v = zip3 lookBehind v lookAhead
242 | where
243 | lookBehind = Nothing `cons` map Just v
244 | lookAhead = drop 1 (map Just v) `snoc` Nothing
245 |
246 | {-# INLINABLE toHashSet #-}
247 | toHashSet :: (Eq a, Hashable a) => Vector a -> HashSet a
248 | toHashSet = foldl' (\set elem -> HashSet.insert elem set) HashSet.empty
249 |
250 | dropWhileLookingAround :: (Maybe a -> a -> Maybe a -> Bool) -> Vector a -> Vector a
251 | dropWhileLookingAround f = map (\(_, v, _) -> v) . dropWhile (\(x,y,z) -> f x y z) . lookAround
252 |
253 | dropWhileEnd :: (a -> Bool) -> Vector a -> Vector a
254 | dropWhileEnd pred v =
255 | case pred `fmap` (v !? (length v - 1)) of
256 | Nothing -> v
257 | Just False -> v
258 | Just True ->
259 | let toDelete count =
260 | case pred `fmap` (v !? (length v - count - 1)) of
261 | Nothing -> count
262 | Just False -> count
263 | Just True -> toDelete (count + 1)
264 | in VG.take (length v - toDelete 1) v
265 |
266 | imapM :: Monad m => (Int -> a -> m b) -> Vector a -> m (Vector b)
267 | {-# INLINE imapM #-}
268 | imapM = VG.imapM
269 |
270 | binarySearchL :: (e -> Ordering) -> Vector e -> Int
271 | binarySearchL cmp vec = loop 0 (length vec)
272 | where
273 | loop !l !u
274 | | u <= l = l
275 | | otherwise =
276 | let k = (u + l) `shiftR` 1
277 | in case cmp (vec ! k) of
278 | LT -> loop (k+1) u
279 | _ -> loop l k
280 |
281 | binarySearchR :: (e -> Ordering) -> Vector e -> Int
282 | binarySearchR cmp vec = loop 0 (length vec)
283 | where
284 | loop !l !u
285 | | u <= l = l
286 | | otherwise =
287 | let k = (u + l) `shiftR` 1
288 | in case cmp (vec ! k) of
289 | GT -> loop l k
290 | _ -> loop (k+1) u
291 |
292 | sortOn :: Ord b => (a -> b) -> Vector a -> Vector a
293 | sortOn f = sortBy (\x y -> compare (f x) (f y))
294 |
295 | sortBy :: (a -> a -> Ordering) -> Vector a -> Vector a
296 | sortBy comp v =
297 | modify (VA.sortBy comp) v
298 |
299 | sort :: Ord a => Vector a -> Vector a
300 | sort v = modify VA.sort v
301 |
302 | groupBy :: (a -> a -> Bool) -> Vector a -> Vector (Vector a)
303 | groupBy eq xs = unfoldrN (VG.length xs) next xs
304 | where
305 | next ys
306 | | VG.null ys = Nothing
307 | | otherwise =
308 | let y = VG.unsafeHead ys
309 | ys' = VG.unsafeTail ys
310 | (l1,l2) = VG.span (eq y) ys'
311 | in Just (y `VG.cons` l1, l2)
312 |
313 | groupOn :: Eq b => (a -> b) -> Vector a -> Vector (b, (Vector a))
314 | groupOn proj xs = unfoldrN (VG.length xs) next xs
315 | where
316 | next ys
317 | | VG.null ys = Nothing
318 | | otherwise =
319 | let y = VG.unsafeHead ys
320 | z = proj y
321 | ys' = VG.unsafeTail ys
322 | (l1,l2) = VG.span (\x -> proj x == z) ys'
323 | in Just ((z , y `VG.cons` l1), l2)
324 |
325 | unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> m (Vector a)
326 | unfoldrM f s = unstreamM (VFM.unfoldrM f s)
327 |
328 | unfoldrNM :: Monad m => Int -> (s -> m (Maybe (a, s))) -> s -> m (Vector a)
329 | unfoldrNM n f s = unstreamM (VFM.unfoldrNM n f s)
330 |
331 | -- | Copied from Data.Vector.Generic as it isn't exported there
332 | unstreamM :: (Monad m) => VFM.Bundle m u a -> m (Vector a)
333 | {-# INLINE unstreamM #-}
334 | unstreamM s =
335 | do xs <- VFM.toList s
336 | return $ unstream $ VFM.unsafeFromList (VFM.size s) xs
337 |
338 | updateVector :: Int -> a -> a -> Vector a -> Vector a
339 | updateVector comp def val vect = updateVectorWith comp def (const val) vect
340 |
341 | updateVectorWith :: Int -> a -> (a -> a) -> Vector a -> Vector a
342 | updateVectorWith comp def val vect =
343 | let vect' =
344 | if comp >= length vect
345 | then vect ++ replicate (comp - (length vect) + 1) def
346 | else vect
347 | in vect' // [(comp, val (vect' ! comp))]
348 |
--------------------------------------------------------------------------------
/strict-data/src/Data/StrictVector/Mutable.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE FlexibleContexts #-}
2 | {-# LANGUAGE FlexibleInstances #-}
3 | {-# LANGUAGE MultiParamTypeClasses #-}
4 | {-# LANGUAGE TypeFamilies #-}
5 | module Data.StrictVector.Mutable
6 | ( module Data.Vector.Generic.Mutable
7 | , MVector(..)
8 | , IOVector, STVector
9 | )
10 | where
11 |
12 | import Control.Monad.ST (RealWorld)
13 | import Data.Vector.Generic.Mutable hiding (MVector)
14 | import qualified Data.Vector.Generic.Mutable as VGM
15 | import qualified Data.Vector.Mutable as VM
16 |
17 | -- | 'MVector' is a strict wrapper around "Data.Vector.Mutable"'s 'Data.Vector.Mutable.MVector'
18 | newtype MVector s a = MVector (VM.MVector s a)
19 |
20 | instance VGM.MVector MVector a where
21 | basicLength (MVector v) = VGM.basicLength v
22 | basicUnsafeSlice n m (MVector v) = MVector $ VGM.basicUnsafeSlice n m v
23 | basicOverlaps (MVector v1) (MVector v2) = VGM.basicOverlaps v1 v2
24 | basicUnsafeNew n = fmap MVector (VGM.basicUnsafeNew n)
25 | basicInitialize (MVector v) = basicInitialize v
26 | basicUnsafeReplicate n x = x `seq` fmap MVector (VGM.basicUnsafeReplicate n x)
27 | basicUnsafeRead (MVector v) n = VGM.basicUnsafeRead v n
28 | basicUnsafeWrite (MVector v) n x = x `seq` VGM.basicUnsafeWrite v n x
29 | basicClear (MVector v) = VGM.basicClear v
30 | basicSet (MVector v) x = x `seq` VGM.basicSet v x
31 | basicUnsafeCopy (MVector v1) (MVector v2) = VGM.basicUnsafeCopy v1 v2
32 | basicUnsafeMove (MVector v1) (MVector v2) = VGM.basicUnsafeMove v1 v2
33 | basicUnsafeGrow (MVector v) n = fmap MVector (VGM.basicUnsafeGrow v n)
34 |
35 | type IOVector = MVector RealWorld
36 | type STVector s = MVector s
37 |
38 |
--------------------------------------------------------------------------------
/strict-data/strict-data.cabal:
--------------------------------------------------------------------------------
1 | name: strict-data
2 | version: 0.2.0.2
3 | synopsis: A collection of commonly used strict data structures
4 | description: A collection of commonly used strict data structures
5 | homepage: https://github.com/factisresearch/opensource#readme
6 | license: BSD3
7 | license-file: LICENSE
8 | author: Alexander Thiemann
9 | , factis research GmbH
10 | maintainer: mail@athiemann.net
11 | copyright: 2016 Alexander Thiemann
12 | , 2017 factis research GmbH
13 | category: Data
14 | build-type: Simple
15 | extra-source-files:
16 | README.md
17 | cabal-version: >=1.10
18 |
19 | library
20 | hs-source-dirs: src
21 | exposed-modules: Data.Choice
22 | , Data.Fail
23 | , Data.Option
24 | , Data.StrictList
25 | , Data.StrictTuple
26 | , Data.StrictVector
27 | , Data.StrictVector.Mutable
28 | , Data.Map.Ordered
29 | , Data.Map.Unordered
30 | other-modules: Data.Fail.Types
31 | , Data.StrictList.Types
32 | build-depends: base >= 4.7 && < 5
33 | , QuickCheck
34 | , aeson
35 | , deepseq
36 | , exceptions
37 | , fail
38 | , hashable
39 | , monad-control
40 | , mtl
41 | , pretty
42 | , resourcet
43 | , strict
44 | , text
45 | , transformers
46 | , transformers-base
47 | , containers >= 0.5
48 | , unordered-containers
49 | , util-plus
50 | , vector
51 | , vector-algorithms
52 | default-language: Haskell2010
53 | ghc-options: -Wall -Wdodgy-imports
54 |
55 | test-suite strict-data-test
56 | type: exitcode-stdio-1.0
57 | hs-source-dirs: test
58 | main-is: Spec.hs
59 | other-modules: Fail
60 | , Option
61 | , StrictList
62 | , StrictVector
63 | , StrictVector.Mutable
64 | , Data.Map.OrderedSpec
65 | build-depends: base >= 4.7 && < 5
66 | , strict-data
67 | , HTF
68 | , vector
69 | , deepseq
70 | , hashable
71 | , containers
72 | ghc-options: -Wall
73 | default-language: Haskell2010
74 |
75 | test-suite strict-data-doctest
76 | type: exitcode-stdio-1.0
77 | hs-source-dirs: test
78 | main-is: Doc.hs
79 | build-depends: base >= 4.7 && < 5
80 | , doctest
81 | default-language: Haskell2010
82 |
83 | source-repository head
84 | type: git
85 | location: https://github.com/factisresearch/opensource.git
86 |
--------------------------------------------------------------------------------
/strict-data/test/Data/Map/OrderedSpec.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ScopedTypeVariables #-}
2 | {-# LANGUAGE ExistentialQuantification #-}
3 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
4 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
5 | module Data.Map.OrderedSpec
6 | ( htf_thisModulesTests
7 | )
8 | where
9 |
10 | import Data.Map.Ordered
11 |
12 | import Data.Traversable
13 | import Prelude hiding (map, lookup, null, filter)
14 | import System.IO.Unsafe (unsafePerformIO)
15 | import Test.Framework
16 | import qualified Control.Exception as E
17 |
18 | newtype OSMapInt = OSMapInt (OSMap Int Int)
19 | deriving (Eq, Show)
20 |
21 | bottom :: a
22 | bottom = undefined
23 |
24 | bottomInt :: Int
25 | bottomInt = bottom
26 |
27 | -- | A modified variant of 'isBottomTimeOut' that lives in the 'IO' monad.
28 | -- (Taken from ChasingBottoms)
29 | isBottom :: a -> Bool
30 | isBottom f =
31 | unsafePerformIO $!
32 | E.evaluate (f `seq` False) `E.catches`
33 | [ E.Handler (\(_ :: E.ArrayException) -> return True)
34 | , E.Handler (\(_ :: E.ErrorCall) -> return True)
35 | , E.Handler (\(_ :: E.NoMethodError) -> return True)
36 | , E.Handler (\(_ :: E.NonTermination) -> return True)
37 | , E.Handler (\(_ :: E.PatternMatchFail) -> return True)
38 | , E.Handler (\(_ :: E.RecConError) -> return True)
39 | , E.Handler (\(_ :: E.RecSelError) -> return True)
40 | , E.Handler (\(_ :: E.RecUpdError) -> return True)
41 | ]
42 |
43 | keyValueByIndex :: Int -> OSMap Int Int -> (Int, Int)
44 | keyValueByIndex i m =
45 | let n = size m
46 | in toList m !! (i `mod` n)
47 |
48 | valueByIndex :: Int -> OSMap Int Int -> Int
49 | valueByIndex i m = snd (keyValueByIndex i m)
50 |
51 | keyByIndex :: Int -> OSMap Int Int -> Int
52 | keyByIndex i m = fst (keyValueByIndex i m)
53 |
54 | instance Arbitrary OSMapInt where
55 | arbitrary =
56 | do l <- arbitrary
57 | return $ OSMapInt $ fromList l
58 |
59 | prop_insertStrictKey :: OSMapInt -> Int -> Bool
60 | prop_insertStrictKey (OSMapInt m) v =
61 | isBottom (insert bottom v m)
62 |
63 | prop_insertStrictValue :: OSMapInt -> Int -> Bool
64 | prop_insertStrictValue (OSMapInt m) k =
65 | isBottom (insert k bottom m)
66 |
67 | prop_deleteStrict :: OSMapInt -> Bool
68 | prop_deleteStrict (OSMapInt m) = isBottom (delete bottom m)
69 |
70 | prop_mapStrict :: OSMapInt -> Int -> Property
71 | prop_mapStrict (OSMapInt m) i =
72 | not (null m) ==>
73 | isBottom $ map (\x -> if x == value then bottom else x) m
74 | where
75 | value = valueByIndex i m
76 |
77 | prop_singletonStrictKey :: Int -> Bool
78 | prop_singletonStrictKey v =
79 | isBottom $ singleton bottomInt v
80 |
81 | prop_singletonStrictValue :: Int -> Bool
82 | prop_singletonStrictValue k =
83 | isBottom $ singleton k bottom
84 |
85 | prop_insertWithStrictKey :: OSMapInt -> Int -> Bool
86 | prop_insertWithStrictKey (OSMapInt m) v =
87 | isBottom $ insertWith (\_ _ -> 0) bottom v m
88 |
89 | prop_insertWithStrictValue1 :: OSMapInt -> Int -> Bool
90 | prop_insertWithStrictValue1 (OSMapInt m) k =
91 | isBottom $ insertWith (\_ _ -> 0) k bottom m
92 |
93 | prop_insertWithStrictValue2 :: OSMapInt -> Int -> Int -> Property
94 | prop_insertWithStrictValue2 (OSMapInt m) v i =
95 | not (null m) ==>
96 | isBottom $ insertWith (\_ old -> if old == value then bottom else v) key v m
97 | where
98 | (key, value) = keyValueByIndex i m
99 |
100 | prop_unionStrictLeft :: OSMapInt -> Bool
101 | prop_unionStrictLeft (OSMapInt m) =
102 | isBottom $ union bottom m
103 |
104 | prop_unionStrictRight :: OSMapInt -> Bool
105 | prop_unionStrictRight (OSMapInt m) =
106 | isBottom $ union m bottom
107 |
108 | prop_differenceStrictLeft :: OSMapInt -> Bool
109 | prop_differenceStrictLeft (OSMapInt m) =
110 | isBottom $ difference bottom m
111 |
112 | prop_differenceStrictRight :: OSMapInt -> Property
113 | prop_differenceStrictRight (OSMapInt m) =
114 | not (null m) ==>
115 | isBottom $ difference m bottom
116 |
117 | prop_intersectionStrictLeft :: OSMapInt -> Bool
118 | prop_intersectionStrictLeft (OSMapInt m) =
119 | isBottom $ intersection bottom m
120 |
121 | prop_intersectionStrictRight :: OSMapInt -> Property
122 | prop_intersectionStrictRight (OSMapInt m) =
123 | not (null m) ==>
124 | isBottom $ intersection m bottom
125 |
126 | prop_insertLookupWithKeyStrictKey :: OSMapInt -> Int -> Bool
127 | prop_insertLookupWithKeyStrictKey (OSMapInt m) v =
128 | isBottom $ snd $ insertLookupWithKey (\_ _ _ -> 0) bottom v m
129 |
130 | prop_insertLookupWithKeyStrictValue1 :: OSMapInt -> Int -> Bool
131 | prop_insertLookupWithKeyStrictValue1 (OSMapInt m) k =
132 | isBottom $ snd $ insertLookupWithKey (\_ _ _ -> 0) k bottom m
133 |
134 | prop_insertLookupWithKeyStrictValue2 :: OSMapInt -> Int -> Int -> Property
135 | prop_insertLookupWithKeyStrictValue2 (OSMapInt m) v i =
136 | not (null m) ==>
137 | isBottom $ snd $ insertLookupWithKey (\_ _ old -> if old == value then bottom else v) key v m
138 | where
139 | (key, value) = keyValueByIndex i m
140 |
141 | prop_updateLookupWithKeyStrictKey :: OSMapInt -> Maybe Int -> Bool
142 | prop_updateLookupWithKeyStrictKey (OSMapInt m) v =
143 | isBottom $ snd $ updateLookupWithKey (\_ _ -> v) bottom m
144 |
145 | prop_updateLookupWithKeyStrictValue1 :: OSMapInt -> Maybe Int -> Int -> Property
146 | prop_updateLookupWithKeyStrictValue1 (OSMapInt m) v i =
147 | not (null m) ==>
148 | isBottom $ snd $ updateLookupWithKey (\_ old -> if old == value then bottom else v) key m
149 | where
150 | (key, value) = keyValueByIndex i m
151 |
152 | prop_updateLookupWithKeyStrictValue2 :: OSMapInt -> Maybe Int -> Int -> Property
153 | prop_updateLookupWithKeyStrictValue2 (OSMapInt m) v i =
154 | not (null m) ==>
155 | isBottom $ snd $ updateLookupWithKey (\_ old -> if old == value then Just bottom else v) key m
156 | where
157 | (key, value) = keyValueByIndex i m
158 |
159 | prop_deleteLookupStrict :: OSMapInt -> Property
160 | prop_deleteLookupStrict (OSMapInt m) =
161 | not (null m) ==>
162 | isBottom $ snd $ deleteLookup bottom m
163 |
164 | prop_alterStrictKey :: OSMapInt -> Bool
165 | prop_alterStrictKey (OSMapInt m) =
166 | isBottom $ alter id bottom m
167 |
168 | prop_alterStrictFun1 :: OSMapInt -> Int -> Property
169 | prop_alterStrictFun1 (OSMapInt m) i =
170 | not (null m) ==>
171 | isBottom $ alter (\_ -> Just bottomInt) key m
172 | where
173 | key = keyByIndex i m
174 |
175 | prop_alterStrictFun2 :: OSMapInt -> Int -> Property
176 | prop_alterStrictFun2 (OSMapInt m) i =
177 | not (null m) ==>
178 | isBottom $ alter (\_ -> bottom) key m
179 | where
180 | key = keyByIndex i m
181 |
182 | prop_differenceWithStrictLeft :: OSMapInt -> Maybe Int -> Bool
183 | prop_differenceWithStrictLeft (OSMapInt m) v =
184 | isBottom $ differenceWith (\_ _ -> v) bottom m
185 |
186 | prop_differenceWithStrictRight :: OSMapInt -> Maybe Int -> Property
187 | prop_differenceWithStrictRight (OSMapInt m) v =
188 | not (null m) ==>
189 | isBottom $ differenceWith (\_ _ -> v) m bottom
190 |
191 | prop_differenceWithStrictFun1 :: OSMapInt -> Int -> Int -> Property
192 | prop_differenceWithStrictFun1 (OSMapInt m) v i =
193 | not (null m) ==>
194 | isBottom $ differenceWith (\_ _ -> Just bottom) m (insert key v m)
195 | where
196 | key = keyByIndex i m
197 |
198 | prop_differenceWithStrictFun2 :: OSMapInt -> Int -> Int -> Property
199 | prop_differenceWithStrictFun2 (OSMapInt m) v i =
200 | not (null m) ==>
201 | isBottom $ differenceWith (\_ _ -> bottom) m (insert key v m)
202 | where
203 | key = keyByIndex i m
204 |
205 | prop_intersectionWithStrictFun2 :: OSMapInt -> Int -> Int -> Property
206 | prop_intersectionWithStrictFun2 (OSMapInt m) v i =
207 | not (null m) ==>
208 | isBottom $ intersectionWith (\_ _ -> bottom) m (insert key v m)
209 | where
210 | key = keyByIndex i m
211 |
212 | prop_updateWithKeyStrictKey :: OSMapInt -> Maybe Int -> Bool
213 | prop_updateWithKeyStrictKey (OSMapInt m) v =
214 | isBottom $ updateWithKey (\_ _ -> v) bottom m
215 |
216 | prop_updateWithKeyStrictValue1 :: OSMapInt -> Maybe Int -> Int -> Property
217 | prop_updateWithKeyStrictValue1 (OSMapInt m) v i =
218 | not (null m) ==>
219 | isBottom $ updateWithKey (\_ old -> if old == value then bottom else v) key m
220 | where
221 | (key, value) = keyValueByIndex i m
222 |
223 | prop_updateWithKeyStrictValue2 :: OSMapInt -> Maybe Int -> Int -> Property
224 | prop_updateWithKeyStrictValue2 (OSMapInt m) v i =
225 | not (null m) ==>
226 | isBottom $ updateWithKey (\_ old -> if old == value then Just bottom else v) key m
227 | where
228 | (key, value) = keyValueByIndex i m
229 |
230 | prop_insertWithKeyStrictKey :: OSMapInt -> Int -> Bool
231 | prop_insertWithKeyStrictKey (OSMapInt m) v =
232 | isBottom $ insertWithKey (\_ _ _ -> 0) bottom v m
233 |
234 | prop_insertWithKeyStrictValue1 :: OSMapInt -> Int -> Bool
235 | prop_insertWithKeyStrictValue1 (OSMapInt m) k =
236 | isBottom $ insertWithKey (\_ _ _ -> 0) k bottom m
237 |
238 | prop_insertWithKeyStrictValue2 :: OSMapInt -> Int -> Int -> Property
239 | prop_insertWithKeyStrictValue2 (OSMapInt m) v i =
240 | not (null m) ==>
241 | isBottom $ insertWithKey (\_ _ old -> if old == value then bottom else v) key v m
242 | where
243 | (key, value) = keyValueByIndex i m
244 |
245 | prop_mapKeysStrict :: OSMapInt -> Int -> Property
246 | prop_mapKeysStrict (OSMapInt m) i =
247 | not (null m) ==>
248 | isBottom $ mapKeys (\k -> if k == key then bottom else k) m
249 | where
250 | key = keyByIndex i m
251 |
252 | prop_fmapStrict :: OSMapInt -> Int -> Property
253 | prop_fmapStrict (OSMapInt m) i =
254 | not (null m) ==>
255 | isBottom $ fmap (\x -> if x == value then bottom else x) m
256 | where
257 | value = valueByIndex i m
258 |
259 | prop_traverseStrict :: OSMapInt -> Int -> Property
260 | prop_traverseStrict (OSMapInt m) i =
261 | not (null m) ==>
262 | isBottom $ fmapDefault (\x -> if x == value then bottom else x) m
263 | where
264 | value = valueByIndex i m
265 |
--------------------------------------------------------------------------------
/strict-data/test/Doc.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 |
3 | import Test.DocTest
4 |
5 | main :: IO ()
6 | main = doctest ["src"]
7 |
8 |
--------------------------------------------------------------------------------
/strict-data/test/Fail.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Fail where
3 |
4 | import Data.Fail
5 |
6 | import Test.Framework
7 |
8 | test_partitionFails :: IO ()
9 | test_partitionFails =
10 | do assertEqual ([]::[Int], []) (partitionFails [])
11 | assertEqual ([1::Int], []) (partitionFails [Ok 1])
12 | assertEqual ([]::[Int], ["bad"]) (partitionFails [Fail "bad"])
13 | assertEqual ([1,2,3::Int], ["bad1", "bad2"])
14 | (partitionFails [Ok 1, Fail "bad1", Ok 2, Ok 3, Fail "bad2"])
15 |
--------------------------------------------------------------------------------
/strict-data/test/Option.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Option where
3 |
4 | import Data.Option
5 |
6 | import Control.Monad (forM_)
7 | import Data.List
8 | import Test.Framework
9 |
10 | test_ord :: IO ()
11 | test_ord =
12 | let list = [None, None, Some "x", Some "x", Some "y"]
13 | in forM_ (permutations list) $ \perm ->
14 | assertEqual list (sort perm)
15 |
16 |
--------------------------------------------------------------------------------
/strict-data/test/Spec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Main where
3 |
4 | import {-@ HTF_TESTS @-} Data.Map.OrderedSpec
5 | import {-@ HTF_TESTS @-} Fail
6 | import {-@ HTF_TESTS @-} Option
7 | import {-@ HTF_TESTS @-} StrictList
8 | import {-@ HTF_TESTS @-} StrictVector
9 | import {-@ HTF_TESTS @-} StrictVector.Mutable
10 |
11 | import Test.Framework
12 |
13 | main :: IO ()
14 | main = htfMain htf_importedTests
15 |
--------------------------------------------------------------------------------
/strict-data/test/StrictList.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TypeOperators #-}
2 | {-# OPTIONS_GHC -F -pgmF htfpp -fno-warn-type-defaults #-}
3 | module StrictList (htf_thisModulesTests) where
4 |
5 | import Data.Option
6 | import Data.StrictList
7 | import Data.StrictTuple
8 |
9 | import Prelude hiding
10 | ( (!!)
11 | , all
12 | , any
13 | , break
14 | , concat
15 | , concatMap
16 | , drop
17 | , dropWhile
18 | , elem
19 | , filter
20 | , length
21 | , lookup
22 | , map
23 | , mapM
24 | , mapM_
25 | , notElem
26 | , null
27 | , replicate
28 | , reverse
29 | , span
30 | , take
31 | , takeWhile
32 | , unzip
33 | , zip
34 | , zipWith
35 | )
36 | import Test.Framework
37 | import qualified Prelude as P
38 |
39 | test_nub :: IO ()
40 | test_nub =
41 | do assertEqual (sl [1, 5, 2] :: SL Int) $ nub (sl [1, 5, 1, 2, 5, 2])
42 |
43 | test_unzip :: IO ()
44 | test_unzip =
45 | do assertEqual ((1:!2:!Nil) :!: ('a':!'b':!Nil)) (unzip ((1 :!: 'a') :! (2 :!: 'b') :! Nil))
46 | assertEqual ((1:!2:!Nil) :!: ('a':!'b':!Nil)) (unzipL [(1:!:'a'),(2:!:'b')])
47 | assertEqual ((1:!2:!Nil) :!: ('a':!'b':!Nil)) (unzipLL [(1,'a'),(2,'b')])
48 |
49 | test_dropWhileEnd :: IO ()
50 | test_dropWhileEnd =
51 | do assertEqual Nil $ dropWhileEnd (<= 1) $ Nil
52 | assertEqual Nil $ dropWhileEnd (<= 1) $ 1 :! Nil
53 | assertEqual (1 :! 2 :! Nil) $ dropWhileEnd (<= 1) $ 1 :! 2 :! 1 :! Nil
54 |
55 |
56 | prop_partition :: [Int] -> Bool
57 | prop_partition l =
58 | let me :: ([Int], [Int])
59 | me = (\(xs, ys) -> (toLazyList xs, toLazyList ys)) $ partition even $ fromLazyList l
60 | in (P.filter even l, P.filter odd l) == me
61 |
62 | test_insert :: IO ()
63 | test_insert =
64 | do assertEqual (1 :! Nil) (insert 1 Nil)
65 | assertEqual (1 :! 2 :! Nil) (insert 1 (2 :! Nil))
66 | assertEqual (1 :! 2 :! Nil) (insert 2 (1 :! Nil))
67 | assertEqual (1 :! 2 :! 3 :! Nil) (insert 2 (1 :! 3 :! Nil))
68 | assertEqual (1 :! 2 :! 3 :! Nil) (insert 3 (1 :! 2 :! Nil))
69 | assertEqual (1 :! 2 :! 2 :! Nil) (insert 2 (1 :! 2 :! Nil))
70 | assertEqual (2 :! 3 :! 1 :! Nil) (insert 2 (3 :! 1 :! Nil))
71 |
72 | test_lookup :: IO ()
73 | test_lookup =
74 | do assertEqual None (lookup True (mk []))
75 | assertEqual (Some 'a') (lookup True (mk [(True, 'a')]))
76 | assertEqual (Some 'a') (lookup True (mk [(False, 'b'),(True, 'a')]))
77 | assertEqual (Some 'a') (lookup True (mk [(False, 'b'),(True, 'a'),(False, 'c')]))
78 | assertEqual (Some 'a') (lookup True (mk [(False, 'b'),(False, 'c'),(True, 'a')]))
79 | assertEqual None (lookup True (mk [(False, 'b')]))
80 | assertEqual None (lookup True (mk [(False, 'a'), (False, 'b')]))
81 | where
82 | mk :: [(Bool,Char)] -> StrictList (Bool :!: Char)
83 | mk = fromLazyList . fmap fromLazyTuple
84 |
85 | prop_take :: Int -> [Int] -> Bool
86 | prop_take l lst =
87 | let me :: [Int]
88 | me = toLazyList $ take l (fromLazyList lst)
89 | in P.take l lst == me
90 |
91 | -- test_sort :: IO ()
92 | -- test_sort =
93 | -- do let list = fromLazyList [1..133]
94 | -- list' <- Cpm.Util.Random.shuffle (toLazyList list)
95 | -- assertEqual list (sort (fromLazyList list'))
96 |
97 | test_headOpt :: IO ()
98 | test_headOpt =
99 | do assertEqual (Some "B") $ headOpt $ fromLazyList ["B","C"]
100 | assertEqual None $ headOpt (Nil :: StrictList ())
101 |
102 | test_lastOpt :: IO ()
103 | test_lastOpt =
104 | do assertEqual (Some 5) $ lastOpt $ fromLazyList [2,4,5]
105 | assertEqual (Some 5) $ lastOpt $ fromLazyList [5]
106 | assertEqual None $ lastOpt $ (Nil :: StrictList ())
107 |
108 | test_findIndex :: IO ()
109 | test_findIndex =
110 | do assertEqual None $ findIndex (== "A") $ fromLazyList ["B","C"]
111 | assertEqual None $ findIndex (== "A") Nil
112 | assertEqual (Some 1) $ findIndex (== "C") $ fromLazyList ["B","C","D"]
113 | assertEqual (Some 0) $ findIndex (/= "C") $ fromLazyList ["B","C","D"]
114 |
115 | test_reverse :: IO ()
116 | test_reverse =
117 | do assertEqual (fromLazyList ["D","C","B"]) (reverse $ fromLazyList ["B","C","D"])
118 | assertEqual (Nil :: StrictList ()) $ reverse Nil
119 |
120 | test_replicate :: IO ()
121 | test_replicate =
122 | do assertEqual (replicate 3 'a') (fromLazyList (P.replicate 3 'a'))
123 | assertEqual (replicate 3 'b') ('b' :! 'b' :! 'b' :! Nil)
124 | assertEqual (replicate 0 'c') Nil
125 | assertEqual (replicate 0 'd') (fromLazyList (P.replicate 0 'e'))
126 |
127 | test_dropWhile :: IO ()
128 | test_dropWhile =
129 | do assertEqual (fromLazyList [5]) $ dropWhile even $ fromLazyList [2,4,5]
130 | assertEqual (fromLazyList [2,4,5]) $ dropWhile odd $ fromLazyList [2,4,5]
131 | assertEqual Nil $ dropWhile (>=1) $ fromLazyList [2,4,5]
132 |
133 | test_stripPrefix :: IO ()
134 | test_stripPrefix =
135 | do assertEqual (Just Nil) $ stripPrefix Nil (Nil :: SL Int)
136 | assertEqual (Just $ 1 :! Nil) $ stripPrefix Nil (1 :! Nil)
137 | assertEqual (Just Nil) $ stripPrefix (1 :! Nil) (1 :! Nil)
138 | assertEqual (Just $ 3 :! Nil) $ stripPrefix (1 :! 2 :! Nil) (1 :! 2 :! 3 :! Nil)
139 | assertEqual Nothing $ stripPrefix (1 :! Nil) Nil
140 |
141 | test_stripSuffix :: IO ()
142 | test_stripSuffix =
143 | do assertEqual (Just Nil) $ stripSuffix Nil (Nil :: SL Int)
144 | assertEqual (Just $ 1 :! Nil) $ stripSuffix Nil (1 :! Nil)
145 | assertEqual (Just Nil) $ stripSuffix (1 :! Nil) (1 :! Nil)
146 | assertEqual (Just $ 1 :! Nil) $ stripSuffix (2 :! 3 :! Nil) (1 :! 2 :! 3 :! Nil)
147 | assertEqual Nothing $ stripSuffix (1 :! Nil) Nil
148 |
149 | test_deleteIdx :: IO ()
150 | test_deleteIdx =
151 | do assertEqual (1 :! Nil) $ deleteIdx (-5) (1 :! Nil)
152 | assertEqual (1 :! 3 :! Nil) $ deleteIdx 1 (1 :! 2 :! 3 :! Nil)
153 | assertEqual Nil $ deleteIdx 0 ("B" :! Nil)
154 | assertEqual (fromLazyList ["a","B","C","D","E"]) $
155 | deleteIdx 5 (fromLazyList ["a","B","C","D","E","Q"])
156 | assertEqual (fromLazyList [1,3,2,4]) $
157 | deleteIdx 4 (fromLazyList [1,3,2,4])
158 |
159 | test_atIdx :: IO ()
160 | test_atIdx =
161 | do assertEqual (Some 1) $ atIdx 0 (1 :! Nil)
162 | assertEqual None $ atIdx 6 (1 :! 2 :! 3 :! Nil)
163 | assertEqual None $ atIdx (-3) ("B" :! Nil)
164 | assertEqual (Some "a") $ atIdx 5 (fromLazyList ["g","q","s","u","xc","a"])
165 |
166 | test_snoc :: IO ()
167 | test_snoc =
168 | do assertEqual (True :! Nil) (snoc Nil True)
169 | assertEqual (False :! True :! Nil) (snoc (False :! Nil) True)
170 |
171 | test_transpose :: IO ()
172 | test_transpose =
173 | do assertEqual (f [[1,4],[2,5],[3,6]]) (transpose (f [[1,2,3],[4,5,6]]))
174 | assertEqual (f [[1,2,3],[4,5],[6]]) (transpose (f [[1,4],[2],[],[3,5,6]]))
175 | where
176 | f = fmap sl . sl
177 |
178 | prop_difference :: SL Int -> SL Int -> Bool
179 | prop_difference xs ys = (xs +!+ ys) \!\ xs == ys
180 |
181 | test_delete :: IO ()
182 | test_delete =
183 | do assertEqual (sl "bnana") (delete 'a' (sl "banana"))
184 | assertEqual Nil (delete 'a' Nil)
185 |
186 | test_merge :: IO ()
187 | test_merge =
188 | assertEqual (sl "abcdef" :: SL Char) (merge (sl "acde") (sl "abdf"))
189 |
--------------------------------------------------------------------------------
/strict-data/test/StrictVector.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-type-defaults -fno-warn-name-shadowing -F -pgmF htfpp #-}
2 | module StrictVector where
3 |
4 | import Data.Option
5 | import Data.StrictVector
6 |
7 | import Control.Exception
8 | import Test.Framework
9 | import qualified Data.List as L
10 | import qualified Data.Vector.Generic as VG
11 |
12 | prop_groupBy :: [(Int,Int)] -> Bool
13 | prop_groupBy l =
14 | let eq (_,x) (_,y) = x == y
15 | res1 = groupBy eq (fromList l)
16 | res2 = fmap fromList (fromList (L.groupBy eq l))
17 | in res2 == res1
18 |
19 | -- prop_groupOn :: [(Int,Int)] -> Bool
20 | -- prop_groupOn l =
21 | -- let proj = snd
22 | -- res1 = groupOn proj (fromList l)
23 | -- res2 = fmap (second fromList) (fromList (L.groupOn proj l))
24 | -- in res2 == res1
25 |
26 | test_lookingAround :: IO ()
27 | test_lookingAround =
28 | do assertEqual expected1 (f input1)
29 | assertEqual [] (f [])
30 | assertEqual [(Nothing,1,Nothing)] (f [1])
31 | assertEqual [(Nothing,1,Just 2),(Just 1,2,Nothing)] (f [1,2])
32 | where
33 | f :: [Int] -> [(Maybe Int, Int, Maybe Int)]
34 | f list = toList (lookAround (fromList list))
35 | input1 = [1,2,3]
36 | expected1 = [ (Nothing, 1, Just 2)
37 | , (Just 1, 2, Just 3)
38 | , (Just 2, 3, Nothing)
39 | ]
40 |
41 | test_uncons :: IO ()
42 | test_uncons =
43 | let atoe :: Int
44 | atoe = 42
45 | in do assertEqual None (uncons $ fromList $ L.drop 1 [atoe])
46 | assertEqual (Some (atoe, fromList [5,2,3]))
47 | (uncons $ fromList [atoe,5,2,3])
48 |
49 | test_binarySearchL :: IO ()
50 | test_binarySearchL =
51 | do assertEqual 0 (binarySearchL (flip compare 0) (fromList [0,1,2]))
52 | assertEqual 1 (binarySearchL (flip compare 1) (fromList [0,1,2]))
53 | assertEqual 1 (binarySearchL (flip compare 1) (fromList [0,1,1,2]))
54 | assertEqual 2 (binarySearchL (flip compare 2) (fromList [0,1,2]))
55 | assertEqual 3 (binarySearchL (flip compare 3) (fromList [0,1,2]))
56 |
57 | test_binarySearchR :: IO ()
58 | test_binarySearchR =
59 | do assertEqual 0 (binarySearchR (flip compare 0) (fromList [1,2,3]))
60 | assertEqual 1 (binarySearchR (flip compare 0) (fromList [0,1,2]))
61 | assertEqual 2 (binarySearchR (flip compare 1) (fromList [0,1,2]))
62 | assertEqual 3 (binarySearchR (flip compare 1) (fromList [0,1,1,2]))
63 | assertEqual 3 (binarySearchR (flip compare 2) (fromList [0,1,2]))
64 | assertEqual 3 (binarySearchR (flip compare 3) (fromList [0,1,2]))
65 |
66 | test_fromListStrict :: IO ()
67 | test_fromListStrict =
68 | do let err = ErrorCall "..."
69 | res <- try $ (VG.fromList [1,2,throw err] :: Vector Int) `seq` return ()
70 | assertEqual (Left err) res
71 |
--------------------------------------------------------------------------------
/strict-data/test/StrictVector/Mutable.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module StrictVector.Mutable where
3 |
4 | import Data.StrictVector.Mutable
5 |
6 | import Control.Exception
7 | import Test.Framework
8 | import qualified Data.Vector.Generic.Mutable as VGM
9 |
10 | test_replicateStrict :: IO ()
11 | test_replicateStrict =
12 | do let err = ErrorCall "..."
13 | res <- try $ (VGM.replicate 10 (throw err :: ()) :: IO (IOVector ()))
14 | assertEqual (Left err) (res >> Right ())
15 |
16 | test_writeStrict :: IO ()
17 | test_writeStrict =
18 | do let err = ErrorCall "..."
19 | vec <- VGM.replicate 1 ()
20 | res <- try $ VGM.write (vec :: IOVector ()) 0 (throw err)
21 | assertEqual (Left err) res
22 |
23 | test_setStrict :: IO ()
24 | test_setStrict =
25 | do let err = ErrorCall "..."
26 | vec <- VGM.replicate 1 ()
27 | res <- try $ VGM.set (vec :: IOVector ()) (throw err)
28 | assertEqual (Left err) res
29 |
--------------------------------------------------------------------------------
/text-plus/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright factis research GmbH (c) 2017
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Kierán Meinhardt nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/text-plus/README.md:
--------------------------------------------------------------------------------
1 | # text-plus
2 |
--------------------------------------------------------------------------------
/text-plus/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/text-plus/src/Data/Text/Plus.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | {-# LANGUAGE ScopedTypeVariables #-}
3 | {-# LANGUAGE ViewPatterns #-}
4 | {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-name-shadowing #-}
5 | module Data.Text.Plus
6 | ( module Data.Text
7 | , module Data.Text.Encoding
8 | , decodeUtf8M
9 | , showText
10 | , readText
11 | , groupOn
12 | , withoutTags
13 | , showTable
14 | , showTableRaw
15 | , filename
16 | , splitOnNoEmpty
17 | , nothingIfEmpty
18 | , noneIfEmpty
19 | , emptyIfNone
20 | , limitTo
21 | , sep, unsep, unsep'
22 | , shorten
23 | , shortenL
24 | , firstToUpper
25 | , shortenLinesL
26 | , lenientDecodeUtf8, lenientDecodeUtf8L
27 | , toLazy
28 | , fromLazy
29 | , indicesOfOccurences
30 | , tokenize
31 | , commonPrefixTotal
32 | , firstLine
33 | , firstParagraph
34 | , escapeXml
35 | , fixed
36 | , fixed'
37 | )
38 | where
39 |
40 | import Data.Fail
41 | import Data.Option
42 | import Safe.Plus
43 |
44 | import Data.Char (isSpace)
45 | import Data.Function
46 | import Data.Maybe
47 | import Data.Monoid
48 | import Data.Text
49 | import Data.Text.Encoding hiding (Decoding(Some))
50 | import Test.QuickCheck
51 | import qualified Data.ByteString as BS
52 | import qualified Data.ByteString.Lazy as BSL
53 | import qualified Data.Char as C
54 | import qualified Data.Foldable as F
55 | import qualified Data.Text as T
56 | import qualified Data.Text.Encoding.Error as TE
57 | import qualified Data.Text.Lazy as TL
58 | import qualified Data.Text.Lazy.Encoding as TLE
59 |
60 | fixed :: Int -> T.Text -> T.Text
61 | fixed = fixed' '0'
62 |
63 | fixed' :: Char -> Int -> T.Text -> T.Text
64 | fixed' ch i s =
65 | let n = i - T.length s
66 | in T.replicate n (T.singleton ch) `T.append` s
67 |
68 | instance Arbitrary T.Text where
69 | arbitrary = T.pack <$> arbitrary
70 | shrink t = T.pack <$> shrink (T.unpack t)
71 |
72 | fromLazy :: TL.Text -> T.Text
73 | fromLazy = TL.toStrict
74 |
75 | toLazy :: T.Text -> TL.Text
76 | toLazy = TL.fromStrict
77 |
78 | showText :: Show a => a -> T.Text
79 | showText = T.pack . show
80 |
81 | showTextL :: Show a => a -> TL.Text
82 | showTextL = TL.pack . show
83 |
84 | readText :: Read a => T.Text -> a
85 | readText = read . T.unpack
86 |
87 | limitTo :: Int -> T.Text -> T.Text
88 | limitTo lim t
89 | | lim <= 5 = t
90 | | T.length t <= lim = t
91 | | otherwise =
92 | T.take (lim - 4) t
93 | <> "..."
94 | <> T.drop (T.length t - 1) t
95 |
96 | groupOn :: Eq a => (Char -> a) -> Text -> [(a, T.Text)]
97 | groupOn _ "" = []
98 | groupOn proj t = (x', x `T.cons` ys) : groupOn proj zs
99 | where
100 | x = T.head t
101 | xs = T.tail t
102 | x' = proj x
103 | (ys,zs) = T.span ((==x') . proj) xs
104 |
105 | firstToUpper :: T.Text -> T.Text
106 | firstToUpper t =
107 | case T.uncons t of
108 | Nothing -> ""
109 | Just (fstChr, rstText) ->
110 | C.toUpper fstChr `cons` rstText
111 |
112 | -- |Removes HTML Tags
113 | withoutTags :: T.Text -> T.Text
114 | withoutTags =
115 | let betweenTags ('<':xs) = inTag xs
116 | betweenTags (x:xs) = x:betweenTags xs
117 | betweenTags [] = []
118 | inTag ('>':xs) = betweenTags xs
119 | inTag ('\'':xs) = inSingQuot xs
120 | inTag ('"':xs) = inDoubleQuot xs
121 | inTag (_:xs) = inTag xs
122 | inTag [] = [] -- incorrect HTML
123 | inSingQuot ('\'':xs) = inTag xs
124 | inSingQuot (_:xs) = inSingQuot xs
125 | inSingQuot [] = [] -- incorrect HTML
126 | inDoubleQuot ('\"':xs) = inTag xs
127 | inDoubleQuot (_:xs) = inDoubleQuot xs
128 | inDoubleQuot [] = [] -- incorrect HTML
129 | in T.pack . betweenTags . T.unpack
130 |
131 | -- indicesOfOccurences needle haystack returns all indices i s.t.
132 | --
133 | -- prop> needle `T.isPrefixOf` (T.drop i haystack)
134 | --
135 | -- Note that: T.breakOnAll - does not return overlapping matches, e.g.
136 | --
137 | -- prop> indicesOfOccurences "edited" "editedited" == [0,4]
138 | --
139 | -- but
140 | --
141 | -- prop> map (T.length . fst) (T.breakOnAll "edited" "editedited") == [0]
142 | --
143 | indicesOfOccurences :: T.Text -> T.Text -> [Int]
144 | indicesOfOccurences needle = go 0
145 | where
146 | go off haystack
147 | | Just (_, matchTail) <- T.uncons match = newOff:go (newOff+1) matchTail
148 | | otherwise = []
149 | where
150 | newOff = off + T.length prefix
151 | (prefix, match) = T.breakOn needle haystack
152 |
153 | -- | Simple tokenizer - that doesn't destroy delimiters, e.g.
154 | --
155 | -- >>> tokenize (T.pack "This is blind-text, with punctuation.")
156 | -- ["This"," ","is"," ","blind","-","text",", ","with"," ","punctuation","."]
157 | --
158 | -- prop> T.concat (tokenize x) == x
159 | --
160 | -- Note: URLs, numbers won't be handled very well.
161 | tokenize :: T.Text -> [T.Text]
162 | tokenize = T.groupBy ((==) `on` C.isAlphaNum)
163 |
164 | -- | @commonPrefixTotal s t@ returns a trippel @(r,st,tt)@ s.t.
165 | -- @
166 | -- s = r `T.append` st, t = r `T.append` tt
167 | -- @
168 | -- such that @r@ is longest possible.
169 | --
170 | -- Note: Contrary to Data.commonPrefix there is no special case when @T.null r@.
171 | commonPrefixTotal :: T.Text -> T.Text -> (T.Text, T.Text, T.Text)
172 | commonPrefixTotal s t = fromMaybe ("", s, t) $ T.commonPrefixes s t
173 |
174 | showTable :: (Traversable t) => [(T.Text, a -> T.Text)] -> t a -> T.Text
175 | showTable headersAccessors rows =
176 | showTableRaw (fmap fst headersAccessors) (fmap (\x -> fmap (($ x) . snd) headersAccessors) rows)
177 |
178 | showTableRaw :: (Traversable t1, Traversable t2) => [T.Text] -> t1 (t2 T.Text) -> T.Text
179 | showTableRaw headers rows = table
180 | where
181 | rows' = fmap (fmap (wrap ' ')) rows
182 | columnHeaders =
183 | fmap (wrap ' ') headers
184 | header = renderRow columnHeaders
185 | headerBodySeperator =
186 | wrap '|' (T.intercalate "+" (fmap (`T.replicate` "-") fieldWidths))
187 | renderRow rowElems =
188 | wrap '|' (T.intercalate "|" (adjust (F.toList rowElems)))
189 | wrap char = T.cons char . (`T.snoc` char)
190 | table =
191 | flip T.snoc '\n' . T.intercalate "\n" $
192 | header : headerBodySeperator : F.toList (fmap renderRow rows')
193 | adjust = Prelude.zipWith (`T.justifyLeft` ' ') fieldWidths
194 | fieldWidths =
195 | Prelude.foldr (Prelude.zipWith (\a b -> max (T.length a) b))
196 | (F.toList (fmap T.length columnHeaders)) (fmap F.toList rows')
197 |
198 | nothingIfEmpty :: T.Text -> Maybe T.Text
199 | nothingIfEmpty t =
200 | if T.null $ T.strip t then Nothing else Just t
201 |
202 | noneIfEmpty :: T.Text -> Option T.Text
203 | noneIfEmpty = maybeToOption . nothingIfEmpty
204 |
205 | emptyIfNone :: Option T.Text -> T.Text
206 | emptyIfNone None = T.empty
207 | emptyIfNone (Some t) = t
208 |
209 | sep :: T.Text -> Char -> T.Text -> T.Text
210 | sep prefix ch suffix
211 | | T.any (==ch) prefix =
212 | safeError ("Oh dear! Won't separate `" ++ T.unpack prefix ++ "' with `" ++ show ch
213 | ++ "' because it contains that character!")
214 | | otherwise = T.concat [prefix, T.singleton ch, suffix]
215 |
216 | unsep' :: Monad m => Char -> T.Text -> m (T.Text, T.Text)
217 | unsep' ch full =
218 | case T.span (/=ch) full of
219 | (prefix, T.uncons -> Just (ch', suffix)) | ch == ch' -> return (prefix, suffix)
220 | _ -> safeFail ("Can't unsep `" ++ T.unpack full ++ "' using `" ++ show ch ++ "'.")
221 |
222 | unsep :: Char -> T.Text -> (T.Text, T.Text)
223 | unsep ch x = safeFromOk (unsep' ch x)
224 |
225 | shorten :: Int -> T.Text -> T.Text
226 | shorten len = TL.toStrict . shortenL len . TL.fromStrict
227 |
228 | shortenL :: Int -> TL.Text -> TL.Text
229 | shortenL (fromIntegral -> maxLen) s =
230 | let actualLen = TL.length s
231 | skipMsg = TL.concat ["... (", showTextL (actualLen - maxLen), " more chars)"]
232 | skipMsgLen = TL.length skipMsg
233 | in if actualLen <= maxLen + skipMsgLen
234 | then s
235 | else TL.concat [TL.take maxLen s, skipMsg]
236 |
237 | shortenLinesL :: Int -> Int -> TL.Text -> TL.Text
238 | shortenLinesL maxLines maxLineLength (Prelude.map (shortenL maxLineLength) . TL.lines -> xs) =
239 | let actualLines = Prelude.length xs
240 | skipMsg = TL.concat ["(", showTextL (actualLines - maxLines), " more lines)"]
241 | lines
242 | | actualLines <= maxLines + 1 = xs
243 | | otherwise = Prelude.take maxLines xs ++ [skipMsg]
244 | in TL.unlines lines
245 |
246 | filename :: T.Text -> T.Text
247 | filename = T.replace "?" "_" . T.replace "/" "_" . T.replace "." "_" . T.replace " " "_"
248 |
249 | splitOnNoEmpty :: T.Text -> T.Text -> [T.Text]
250 | splitOnNoEmpty break t =
251 | Prelude.filter (/= "") $ T.splitOn break t
252 |
253 | lenientDecodeUtf8 :: BS.ByteString -> T.Text
254 | lenientDecodeUtf8 = decodeUtf8With TE.lenientDecode
255 |
256 | lenientDecodeUtf8L :: BSL.ByteString -> TL.Text
257 | lenientDecodeUtf8L = TLE.decodeUtf8With TE.lenientDecode
258 |
259 | decodeUtf8M :: BS.ByteString -> Fail T.Text
260 | decodeUtf8M bs =
261 | case decodeUtf8' bs of
262 | Left (TE.DecodeError err (Just w8)) ->
263 | Fail $
264 | "Failed decoding " ++ show bs ++ " as UTF-8 on character " ++ show w8 ++ ": " ++ err
265 | Left (TE.DecodeError err Nothing) ->
266 | Fail $ "Failed decoding " ++ show bs ++ " as UTF-8: " ++ err
267 | Left _ -> safeError "Never used according to documentation."
268 | Right txt -> Ok txt
269 |
270 | firstParagraph :: T.Text -> T.Text
271 | firstParagraph =
272 | T.unlines . Prelude.takeWhile (not . endOfParagraph) . T.lines
273 | where
274 | endOfParagraph = T.all isSpace
275 |
276 | firstLine :: T.Text -> T.Text
277 | firstLine = T.takeWhile (/='\n')
278 |
279 | escapeXml :: T.Text -> T.Text
280 | escapeXml = T.concatMap escape
281 | where
282 | escape c =
283 | case c of
284 | '<' -> "<"
285 | '>' -> ">"
286 | '&' -> "&"
287 | '"' -> """
288 | '\'' -> "'"
289 | c -> T.singleton c
290 |
--------------------------------------------------------------------------------
/text-plus/test/Data/Text/PlusSpec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Data.Text.PlusSpec (htf_thisModulesTests) where
4 |
5 | import Data.Text.Plus
6 |
7 | import Test.Framework
8 | import qualified Data.Char as C
9 | import qualified Data.Text as T
10 |
11 | test_groupOn :: IO ()
12 | test_groupOn =
13 | do assertEqual [] $ groupOn id ""
14 | assertEqual [(False, "abc"), (True, "123"), (False, "def")] $ groupOn C.isDigit "abc123def"
15 |
16 | test_firstToUpper :: IO ()
17 | test_firstToUpper =
18 | do assertEqual "Hallo" $ firstToUpper "hallo"
19 | assertEqual "HAllo" $ firstToUpper "HAllo"
20 | assertEqual "" $ firstToUpper ""
21 |
22 | test_withoutTags :: IO ()
23 | test_withoutTags =
24 | do assertEqual "Hello laboratory report!"
25 | (withoutTags $ "Hello
laboratory
report!
")
26 | assertEqual "Hello laboratory report!"
27 | (withoutTags $ "x<\">Hello
laboratory
report!")
28 | assertEqual "Hello report!"
29 | (withoutTags $ "Hello report!
")
30 |
31 | prop_indicesOverlapping :: Property
32 | prop_indicesOverlapping = once $ prop_indices "aba" "ababa"
33 |
34 | prop_indicesThree :: Property
35 | prop_indicesThree = once $ prop_indices "a" "ababa"
36 |
37 | prop_indicesNone :: Property
38 | prop_indicesNone = once $ prop_indices "foo" "ababa"
39 |
40 | prop_indicesSingle :: Property
41 | prop_indicesSingle = once $ prop_indices "bab" "ababa"
42 |
43 | prop_indices :: T.Text -> T.Text -> Property
44 | prop_indices n h = not (T.null n) ==>
45 | (indicesOfOccurences n h === [ i | i <- [0..(T.length h - 1)], n `T.isPrefixOf` (T.drop i h)])
46 |
47 | test_tokenize :: IO ()
48 | test_tokenize =
49 | assertEqual ["This"," ","is"," ","blind","-","text",", ","with", " ", "punctuation", "."] $
50 | tokenize "This is blind-text, with punctuation."
51 |
52 | test_tokenizeWithNum :: IO ()
53 | test_tokenizeWithNum =
54 | assertEqual ["100"," ","sheep"] $ tokenize "100 sheep"
55 |
56 | test_tokenizeEmpty :: IO ()
57 | test_tokenizeEmpty =
58 | assertEqual [] $ tokenize ""
59 |
60 | test_showTableRaw :: IO ()
61 | test_showTableRaw =
62 | let header = ["Col1", "Col2", "Col3"]
63 | table = [["longfield", "-", ""], ["short", "longfield", ""]]
64 | in assertEqual (showTableRaw header table) $
65 | T.concat
66 | [ "| Col1 | Col2 | Col3 |\n"
67 | , "|-----------+-----------+------|\n"
68 | , "| longfield | - | |\n"
69 | , "| short | longfield | |\n"
70 | ]
71 |
72 | test_shortenL :: IO ()
73 | test_shortenL =
74 | do assertEqual "123456789012345678901" (shortenL 20 "123456789012345678901")
75 | assertEqual "123456789012345678901" (shortenL 21 "123456789012345678901")
76 | assertEqual "123456789012345678901" (shortenL 22 "123456789012345678901")
77 | assertEqual "123456789012345678901" (shortenL 2 "123456789012345678901")
78 | assertEqual "1... (20 more chars)" (shortenL 1 "123456789012345678901")
79 | assertEqual "... (21 more chars)" (shortenL 0 "123456789012345678901")
80 |
81 | test_shortenLinesL1 :: IO ()
82 | test_shortenLinesL1 =
83 | assertEqual outp (shortenLinesL 2 1 inp)
84 | where
85 | outp =
86 | "1... (20 more chars)\n\
87 | \1... (20 more chars)\n\
88 | \(4 more lines)\n"
89 | inp =
90 | "123456789012345678901\n123456789012345678901\n123456789012345678901\n\
91 | \123456789012345678901\n123456789012345678901\n123456789012345678901"
92 |
93 | test_shortenLinesL2 :: IO ()
94 | test_shortenLinesL2 =
95 | assertEqual outp (shortenLinesL 0 0 inp)
96 | where
97 | outp = "(6 more lines)\n"
98 | inp =
99 | "123456789012345678901\n123456789012345678901\n123456789012345678901\n\
100 | \123456789012345678901\n123456789012345678901\n123456789012345678901"
101 |
102 | test_filename :: IO ()
103 | test_filename =
104 | do assertEqual "Foo" (filename "Foo")
105 | assertEqual "Foo_Bar" (filename "Foo Bar")
106 | assertEqual "Foo_Bar" (filename "Foo/Bar")
107 | assertEqual "Foo_Bar" (filename "Foo.Bar")
108 | assertEqual "Foo_Bar" (filename "Foo?Bar")
109 |
110 | test_sepUnsep :: IO ()
111 | test_sepUnsep =
112 | do assertEqual ("foo|bar") (sep "foo" '|' "bar")
113 | assertEqual ("foo", "bar") (unsep '|' "foo|bar")
114 | assertEqual ("foo", "bar|baz") (unsep '|' "foo|bar|baz")
115 |
116 | test_firstLine :: IO ()
117 | test_firstLine =
118 | do assertEqual "Hello World" (firstLine "Hello World")
119 | assertEqual "Hello World" (firstLine "Hello World\nsecond line")
120 | assertEqual "Hello World " (firstLine "Hello World \nsecond line")
121 |
122 | test_firstParagraph :: IO ()
123 | test_firstParagraph =
124 | do assertEqual "Hello\nWorld\n" $ firstParagraph "Hello\nWorld"
125 | assertEqual "Hello\nWorld\n" $ firstParagraph "Hello\nWorld\n"
126 | assertEqual "Hello\nWorld\n" $ firstParagraph "Hello\nWorld\n\nSecond paragraph.\n"
127 | assertEqual "Hello\nWorld\n" $ firstParagraph "Hello\nWorld\n\nSecond paragraph."
128 | assertEqual "Hello\nWorld\n" $ firstParagraph "Hello\nWorld\n\nSecond paragraph.\n\nThird."
129 | assertEqual "Hello\nWorld\n" $ firstParagraph "Hello\nWorld\n \nSecond paragraph."
130 |
131 | test_escapeXml :: IO ()
132 | test_escapeXml =
133 | do assertEqual "foobar" (escapeXml "foobar")
134 | assertEqual "Hallo <Welt>" (escapeXml "Hallo ")
135 | assertEqual "Hallo 'Welt>" (escapeXml "Hallo 'Welt>")
136 |
--------------------------------------------------------------------------------
/text-plus/test/Spec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Main where
3 |
4 | import {-@ HTF_TESTS @-} Data.Text.PlusSpec
5 |
6 | import Test.DocTest
7 | import Test.Framework
8 |
9 | main :: IO ()
10 | main =
11 | do doctest ["src"]
12 | htfMain htf_importedTests
13 |
--------------------------------------------------------------------------------
/text-plus/text-plus.cabal:
--------------------------------------------------------------------------------
1 | name: text-plus
2 | version: 0.1.0.1
3 | synopsis: Utils for text
4 | description: Utils for text
5 | homepage: https://github.com/factisresearch/opensource#readme
6 | license: BSD3
7 | license-file: LICENSE
8 | author: factis research GmbH
9 | maintainer: kieran.meinhardt@gmail.com
10 | copyright: 2017 factis research GmbH
11 | category: Data
12 | build-type: Simple
13 | extra-source-files: README.md
14 | cabal-version: >=1.10
15 |
16 | library
17 | hs-source-dirs: src
18 | exposed-modules: Data.Text.Plus
19 | build-depends: base >= 4.7 && < 5
20 | , QuickCheck
21 | , bytestring
22 | , pretty
23 | , strict-data
24 | , text
25 | , util-plus
26 | default-language: Haskell2010
27 |
28 | test-suite text-plus-test
29 | type: exitcode-stdio-1.0
30 | hs-source-dirs: test
31 | main-is: Spec.hs
32 | other-modules: Data.Text.PlusSpec
33 | build-depends: base
34 | , HTF
35 | , doctest
36 | , text
37 | , text-plus
38 | ghc-options: -threaded -rtsopts -with-rtsopts=-N
39 | default-language: Haskell2010
40 |
41 | source-repository head
42 | type: git
43 | location: https://github.com/factisresearch/opensource.git
44 |
--------------------------------------------------------------------------------
/util-plus/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright factis research GmbH (c) 2017
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Alexander Thiemann nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/util-plus/README.md:
--------------------------------------------------------------------------------
1 | # A collection of commonly used utils
2 |
3 | [](https://circleci.com/gh/factisresearch/opensource-mono)
4 |
5 | This package contains utility functions for data types of basic libraries and common general purpose combinators.
6 |
--------------------------------------------------------------------------------
/util-plus/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/util-plus/src/Control/Applicative/Plus.hs:
--------------------------------------------------------------------------------
1 | module Control.Applicative.Plus
2 | ( withOptional
3 | , optional'
4 | , module Control.Applicative
5 | )
6 | where
7 |
8 | import Control.Applicative
9 | import Control.Monad
10 | import qualified Data.Foldable as F
11 |
12 | {-# SPECIALIZE
13 | withOptional :: (a -> (b -> m c) -> m c) -> Maybe a -> (Maybe b -> m c) -> m c
14 | #-}
15 | withOptional ::
16 | (Foldable t, Alternative t)
17 | => (a -> (b -> m c) -> m c)
18 | -> t a
19 | -> (t b -> m c)
20 | -> m c
21 | withOptional withReq wrappedVal go =
22 | do let runAction =
23 | flip fmap wrappedVal $ \val ->
24 | withReq val $ \inner -> go (pure inner)
25 | emptyCase = go empty
26 | actionOrEmptyCase =
27 | F.foldl' (\_ a -> a) emptyCase runAction
28 | actionOrEmptyCase
29 |
30 | -- | A generalized version of 'optional' that works with 'Option' for example
31 | optional' :: (MonadPlus t, Alternative f) => f a -> f (t a)
32 | optional' x =
33 | flip fmap (optional x) $ \val ->
34 | case val of
35 | Just ok -> pure ok
36 | Nothing -> empty
37 |
--------------------------------------------------------------------------------
/util-plus/src/Data/List/Plus.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2 | {-# LANGUAGE ScopedTypeVariables #-}
3 | module Data.List.Plus
4 | ( module Data.List
5 | , catMaybes
6 | , chunksOf
7 | , extractLast
8 | , groupOn
9 | , groupOn'
10 | , groupOnSort
11 | , groupOnSort'
12 | , groupBySort
13 | , groupUnsortedOn
14 | , headM
15 | , lastElems
16 | , lastM
17 | , lookupM
18 | , makeMapping
19 | , mapMaybe
20 | , maximumM
21 | , merge
22 | , middle
23 | , minimumM
24 | , monotone
25 | , nubMerge
26 | , prefixesAndSuffixes
27 | , sconcatBy
28 | , spanTailRec
29 | , stripSuffix
30 | , tryStripPrefix
31 | , ungroupMay
32 | , withLast
33 | )
34 | where
35 |
36 | import Safe.Plus
37 |
38 | import Control.Arrow (first, second)
39 | import Data.Function
40 | import Data.Hashable (Hashable)
41 | import Data.List
42 | import Data.Maybe (mapMaybe, catMaybes)
43 | import qualified Data.Foldable as F
44 | import qualified Data.HashSet as HashSet
45 | import qualified Data.List as L
46 | import qualified Data.List.NonEmpty as NL
47 | import qualified Data.Semigroup as S
48 |
49 | -- | Computes the element in the middle of the list
50 | -- If the list has an even number of elements, you will get the element after the middle of the
51 | -- list.
52 | middle :: [a] -> Maybe a
53 | middle xs =
54 | case drop (length xs `div` 2) xs of
55 | [] -> Nothing
56 | x:_ -> Just x
57 |
58 | -- O(n) requires a list sorted by the group key
59 | groupOn :: Eq b => (a -> b) -> [a] -> [(b,[a])]
60 | groupOn _ [] = []
61 | groupOn proj (x:xs) = (x', (x:ys)) : groupOn proj zs
62 | where
63 | x' = proj x
64 | (ys,zs) = span ((==x') . proj) xs
65 |
66 | -- First sort and then group the list
67 | groupOnSort :: Ord b => (a -> b) -> [a] -> [(b,[a])]
68 | groupOnSort proj = groupOn proj . L.sortOn proj
69 |
70 | -- First sort and then group the list
71 | groupBySort :: Ord b => (b -> b -> Ordering) -> (a -> b) -> [a] -> [(b,[a])]
72 | groupBySort cmp proj = groupOn proj . L.sortBy (\x y -> cmp (proj x) (proj y))
73 |
74 | -- O(n^2)
75 | -- Group the list, but don't sort it
76 | groupUnsortedOn :: forall a b. Eq b => (a -> b) -> [a] -> [(b, [a])]
77 | groupUnsortedOn proj =
78 | L.foldl' (addToGroups) []
79 | where
80 | addToGroups :: [(b, [a])] -> a -> [(b, [a])]
81 | addToGroups m val =
82 | let key = proj val
83 | in case break ((== key) . fst) m of
84 | (_, []) -> m ++ [(key, [val])]
85 | (p, f:r) -> p ++ [(fst f, snd f ++ [val])] ++ r
86 |
87 | -- O(n) requires a list sorted by the group key
88 | groupOn' :: Eq b => (a -> (b,c)) -> [a] -> [(b,[c])]
89 | groupOn' proj = map (second (map (snd . proj))) . groupOn (fst . proj)
90 |
91 | groupOnSort' :: Ord b => (a -> (b,c)) -> [a] -> [(b,[c])]
92 | groupOnSort' proj = groupOn' proj . L.sortOn (fst . proj)
93 |
94 | sconcatBy :: (Ord b, Foldable f, S.Semigroup s) => (a -> b) -> (a -> s) -> f a -> [(b,s)]
95 | sconcatBy p1 p2 =
96 | fmap proj
97 | . NL.groupBy ((==) `on` p1)
98 | . L.sortOn p1
99 | . F.toList
100 | where
101 | proj gr = (p1 $ NL.head gr, S.sconcat $ NL.map p2 gr)
102 |
103 | extractLast :: a -> [a] -> ([a], a)
104 | extractLast x xs =
105 | case reverse xs of
106 | [] -> ([], x)
107 | y:ys -> (x : reverse ys, y)
108 |
109 | lastElems :: Int -> [a] -> [a]
110 | lastElems n =
111 | reverse . take n . reverse
112 |
113 | headM :: Monad m => [a] -> m a
114 | headM xs =
115 | case xs of
116 | [] -> safeFail "Cannot compute head of empty list"
117 | x:_ -> return x
118 |
119 | lastM :: Monad m => [a] -> m a
120 | lastM xs =
121 | case xs of
122 | [] -> safeFail "Cannot compute last of empty list"
123 | x:[] -> return x
124 | _:xs -> lastM xs
125 |
126 | withLast :: (a -> a) -> [a] -> [a]
127 | withLast _ [] = []
128 | withLast f [x] = [f x]
129 | withLast f (x:xs) = x : withLast f xs
130 |
131 | minimumM :: (Monad m, Ord a) => [a] -> m a
132 | minimumM xs =
133 | case xs of
134 | [] -> safeFail "Cannot compute minimum of empty list"
135 | y:ys -> return $ L.foldl' min y ys
136 |
137 | maximumM :: (Monad m, Ord a) => [a] -> m a
138 | maximumM xs =
139 | case xs of
140 | [] -> safeFail "Connot compute maximum of empty list"
141 | y:ys -> return $ L.foldl' max y ys
142 |
143 | lookupM :: (Eq a, Monad m) => (a -> String) -> a -> [(a,b)] -> m b
144 | lookupM str x xs =
145 | case lookup x xs of
146 | Nothing ->
147 | safeFail ("Lookup of " ++ str x ++ " failed. Valid values are: "
148 | ++ show (map (str . fst) xs))
149 | Just a ->
150 | return a
151 |
152 | ungroupMay :: [(a,[b])] -> Maybe [(a,b)]
153 | ungroupMay [] = Just []
154 | ungroupMay ((_,[]):_) = Nothing
155 | ungroupMay ((a,bs):rest) =
156 | do r <- ungroupMay rest
157 | return (map ((,) a) bs ++ r)
158 |
159 | -- Returns false if and only if there are elements in decreasing order in the list.
160 | monotone :: (Ord a) => [a] -> Bool
161 | monotone (x0:x1:xs)
162 | | x0 <= x1 = monotone (x1:xs)
163 | | otherwise = False
164 | monotone _ = True
165 |
166 | -- makeMapping takes a list of pairs and create a list of key-value pairs
167 | -- such that each key appears only once in the result list. Moreover,
168 | -- the result list contains the pairs in the same order as the input list.
169 | -- Example: [(k1, v1), (k2, v2), (k1, v3)] --> [(k2, v2), (k1, v3)]
170 | makeMapping :: (Eq a, Hashable a) => [(a, b)] -> [(a, b)]
171 | makeMapping l =
172 | go (reverse l) HashSet.empty []
173 | where
174 | go [] _ acc = acc
175 | go (x@(k, _) : xs) done acc =
176 | if k `HashSet.member` done
177 | then go xs done acc
178 | else go xs (HashSet.insert k done) (x:acc)
179 |
180 | -- | Merge two sorted list so that the resulting list is sorted as well.
181 | -- and contains all elements from one of the lists.
182 | -- The length of the resulting list is the sum of the lengths of the given lists.
183 | merge :: Ord a => [a] -> [a] -> [a]
184 | merge [] ys = ys
185 | merge xs [] = xs
186 | merge (x:xs) (y:ys)
187 | | x == y = x:y:(merge xs ys)
188 | | x < y = x:(merge xs (y:ys))
189 | | otherwise = y:(merge (x:xs) ys)
190 |
191 | -- | Merge the two sorted lists and remove all duplicates.
192 | nubMerge :: Ord a => [a] -> [a] -> [a]
193 | nubMerge xs ys = nubSorted $ merge xs ys
194 | where
195 | nubSorted :: Ord a => [a] -> [a]
196 | nubSorted = foldr consUniqSorted []
197 |
198 | consUniqSorted :: Ord a => a -> [a] -> [a]
199 | consUniqSorted x [] = [x]
200 | consUniqSorted x ys@(y:_) | x == y = ys
201 | | otherwise = x:ys
202 |
203 | chunksOf :: Int -> [a] -> [[a]]
204 | chunksOf n xs =
205 | case splitAt n xs of
206 | ([], _) -> []
207 | (first, rest) -> first : chunksOf n rest
208 |
209 | stripSuffix :: (Eq a) => [a] -> [a] -> Maybe [a]
210 | stripSuffix s = (fmap reverse) . L.stripPrefix (reverse s) . reverse
211 |
212 | prefixesAndSuffixes :: [a] -> [([a],[a])]
213 | prefixesAndSuffixes a =
214 | case a of
215 | [] -> [([], [])]
216 | (a : r) -> ([], a : r) : map (first (a:)) (prefixesAndSuffixes r)
217 |
218 | -- | Strips as elements of a given prefix list as possible. Stops stripping
219 | -- if the prefix doesn't match anymore or is exhausted and returns the remaining
220 | -- string.
221 | tryStripPrefix :: Eq a => [a] -> [a] -> [a]
222 | tryStripPrefix [] xs = xs
223 | tryStripPrefix _ [] = []
224 | tryStripPrefix (x:xs) yys@(y:ys)
225 | | x == y = tryStripPrefix xs ys
226 | | otherwise = yys
227 |
228 | spanTailRec :: (a -> Bool) -> [a] -> ([a], [a])
229 | spanTailRec p xs = go ([], xs)
230 | where go (xs,[]) = (reverse xs, [])
231 | go (xs,(y:ys))
232 | | p y = go (y : xs, ys)
233 | | otherwise = (reverse xs, y:ys)
234 |
--------------------------------------------------------------------------------
/util-plus/src/GHC/Stack/Plus.hs:
--------------------------------------------------------------------------------
1 | {-| Helper functions for dealing with call stacks and source locations.
2 | -}
3 | module GHC.Stack.Plus
4 | ( callerLocation
5 | , callerFile
6 | , callerLine
7 | , adjustSourceFilePath
8 | )
9 | where
10 |
11 | import GHC.Stack
12 | import Safe (tailSafe, lastNote)
13 |
14 | adjustSourceFilePath :: FilePath -> FilePath
15 | adjustSourceFilePath = tailSafe . dropWhile (/='/') . drop 1 . dropWhile (/= '/')
16 |
17 | callerLocation :: (HasCallStack) => String
18 | callerLocation = callerFile ++ ":" ++ show callerLine
19 |
20 | -- | The filename of the first caller which called a function with implicit
21 | -- parameter @(callStack :: 'CallStack')@.
22 | callerFile :: (HasCallStack) => String
23 | callerFile = srcLocFile . callerSrcLoc $ callStack
24 |
25 | -- | The line number of the first caller which called a function with
26 | -- implicit parameter @(callStack :: 'CallStack')@.
27 | callerLine :: (HasCallStack) => Int
28 | callerLine = srcLocStartLine . callerSrcLoc $ callStack
29 |
30 | callerSrcLoc :: CallStack -> SrcLoc
31 | callerSrcLoc = snd . caller
32 |
33 | caller :: CallStack -> (String, SrcLoc)
34 | caller = lastNote "Empty CallStack?!" . getCallStack
35 |
--------------------------------------------------------------------------------
/util-plus/src/Safe/Plus.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2 | module Safe.Plus
3 | ( callerFile
4 | , callerLine
5 | , callerLocation
6 | , safeDigitToInt
7 | , safeRead
8 | , readNoteVerbose
9 | , safeFromJust
10 | , safeFromJustNote
11 | , safeHead
12 | , safeTail
13 | , safeInit
14 | , safeLast
15 | , safeMaximum
16 | , safeMinimum
17 | , safeHeadNote
18 | , safeFromRight
19 | , fromRightNote
20 | , safeFromLeft
21 | , fromLeftNote
22 | , safeAtArray
23 | , atArrayNote
24 | , safeAt
25 | , safeError
26 | , safeFail
27 | , safeUndef
28 | )
29 | where
30 |
31 | import Data.Array.IArray
32 | import Data.Char
33 | import Data.Monoid
34 | import GHC.Stack
35 | import GHC.Stack.Plus
36 | import Safe
37 |
38 | -- | Convert a single digit 'Char' to the corresponding 'Int'.
39 | -- This function fails unless its argument satisfies 'isHexDigit',
40 | -- but recognises both upper and lower-case hexadecimal digits
41 | -- (i.e. @\'0\'@..@\'9\'@, @\'a\'@..@\'f\'@, @\'A\'@..@\'F\'@).
42 | safeDigitToInt :: Monad m => Char -> m Int
43 | safeDigitToInt c
44 | | isDigit c = return $ ord c - ord '0'
45 | | c >= 'a' && c <= 'f' = return $ ord c - ord 'a' + 10
46 | | c >= 'A' && c <= 'F' = return $ ord c - ord 'A' + 10
47 | | otherwise = safeFail ("Char.safeDigitToInt: not a digit " ++ show c)
48 |
49 | safeUndef :: (HasCallStack) => a
50 | safeUndef = safeError "undefined!"
51 |
52 | safeRead :: (HasCallStack, Read a) => String -> a
53 | safeRead = readNoteVerbose callerLocation
54 |
55 | readNoteVerbose :: Read a => String -> String -> a
56 | readNoteVerbose msg s =
57 | case [x | (x,t) <- reads s, ("","") <- lex t] of
58 | [x] -> x
59 | [] -> error $ "Prelude.read: no parse, " ++ msg ++ ", on " ++ prefix
60 | _ -> error $ "Prelude.read: ambiguous parse, " ++ msg ++ ", on " ++ prefix
61 | where
62 | prefix = '\"' : a ++ if null b then "\"" else "..."
63 | where (a,b) = splitAt 1024 s
64 |
65 | safeFromJust :: (HasCallStack) => Maybe a -> a
66 | safeFromJust = Safe.fromJustNote callerLocation
67 |
68 | safeFromJustNote :: (HasCallStack) => String -> Maybe a -> a
69 | safeFromJustNote s = Safe.fromJustNote (callerLocation ++ ": " ++ s)
70 |
71 | safeFail :: (HasCallStack, Monad m) => String -> m a
72 | safeFail x = fail (callerLocation ++ ": FAIL: " ++ x)
73 |
74 | safeHead :: (HasCallStack) => [a] -> a
75 | safeHead = Safe.headNote callerLocation
76 |
77 | safeTail :: (HasCallStack) => [a] -> [a]
78 | safeTail = Safe.tailNote callerLocation
79 |
80 | safeInit :: (HasCallStack) => [a] -> [a]
81 | safeInit = Safe.initNote callerLocation
82 |
83 | safeLast :: (HasCallStack) => [a] -> a
84 | safeLast = Safe.lastNote callerLocation
85 |
86 | safeMaximum :: (HasCallStack, Ord a) => [a] -> a
87 | safeMaximum = Safe.maximumNote callerLocation
88 |
89 | safeMinimum :: (HasCallStack, Ord a) => [a] -> a
90 | safeMinimum = Safe.minimumNote callerLocation
91 |
92 | safeHeadNote :: (HasCallStack) => String -> [a] -> a
93 | safeHeadNote x = Safe.headNote (callerLocation ++ ": " ++ x)
94 |
95 | safeFromRight :: (HasCallStack) => Either a b -> b
96 | safeFromRight = fromRightNote callerLocation
97 |
98 | fromRightNote :: String -> Either a b -> b
99 | fromRightNote msg (Left _) = error $ "fromRight got a left value: " ++ msg
100 | fromRightNote _ (Right x) = x
101 |
102 | safeFromLeft :: (HasCallStack) => Either a b -> a
103 | safeFromLeft = fromLeftNote callerLocation
104 |
105 | fromLeftNote :: String -> Either a b -> a
106 | fromLeftNote msg (Right _) = safeError $ "fromLeft got a right value: " ++ msg
107 | fromLeftNote _ (Left x) = x
108 |
109 | safeAtArray :: (HasCallStack, IArray a e, Ix i, Show i) => a i e -> i -> e
110 | safeAtArray = atArrayNote callerLocation
111 |
112 | atArrayNote :: (IArray a e, Ix i, Show i) => String -> a i e -> i -> e
113 | atArrayNote msg array index
114 | | inRange arrayBounds index = array ! index
115 | | otherwise =
116 | error $ concat
117 | [ "lookup at index ", show index, " in an array was outside of its bounds "
118 | , show arrayBounds, ": ", msg
119 | ]
120 | where
121 | arrayBounds = bounds array
122 |
123 | safeAt :: (HasCallStack) => [a] -> Int -> a
124 | safeAt = Safe.atNote callerLocation
125 |
126 | safeError :: (HasCallStack) => String -> a
127 | safeError err = error $ callerLocation <> ": " <> err
128 |
--------------------------------------------------------------------------------
/util-plus/test/Data/List/PlusSpec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp -fno-warn-type-defaults #-}
2 | module Data.List.PlusSpec
3 | ( htf_thisModulesTests
4 | )
5 | where
6 |
7 | import Data.List.Plus
8 | import Test.Framework
9 | import Test.QuickCheck.Function
10 | import qualified Data.List.Plus as L
11 | import qualified Data.Map.Strict as Map
12 | import qualified Data.Set as Set
13 |
14 | prop_spanTailRec :: Fun Int Bool -> [Int] -> Bool
15 | prop_spanTailRec (Fun _ f) lst = L.span f lst == spanTailRec f lst
16 |
17 | test_middle :: IO ()
18 | test_middle =
19 | do assertNothing (middle ([] :: [Int]))
20 | assertEqual (Just 1) (middle [1])
21 | assertEqual (Just 2) (middle [1,2])
22 | assertEqual (Just 2) (middle [1,2,3])
23 | assertEqual (Just 3) (middle [1,2,3,4])
24 |
25 | test_tryStripPrefix :: IO ()
26 | test_tryStripPrefix =
27 | do assertEqual "foobar" (tryStripPrefix "" "foobar")
28 | assertEqual "" (tryStripPrefix "foobar" "")
29 | assertEqual "z" (tryStripPrefix "foobar" "foobaz")
30 | assertEqual "baz" (tryStripPrefix "foobar" "foobarbaz")
31 | assertEqual "" (tryStripPrefix "foobarbaz" "foobar")
32 |
33 | test_groupUnsortedOn :: IO ()
34 | test_groupUnsortedOn =
35 | do assertEqual [] $ groupUnsortedOn id ([] :: [()])
36 | assertEqual [((), [()])] $ groupUnsortedOn id [()]
37 | assertEqual [((), [(), ()])] $ groupUnsortedOn id [(), ()]
38 | assertEqual [(1, [1]), (0, [2])] $ groupUnsortedOn (`mod` 2) [1, 2]
39 | assertEqual [(0, [2]), (1, [1])] $ groupUnsortedOn (`mod` 2) [2, 1]
40 | assertEqual [(1, [1, 3]), (0, [2, 4])] $ groupUnsortedOn (`mod` 2) [1, 2, 3, 4]
41 | assertEqual [(1, [1, 3]), (0, [2, 4])] $ groupUnsortedOn (`mod` 2) [1, 2, 4, 3]
42 | assertEqual [(0, [2, 4]), (1, [1, 3])] $ groupUnsortedOn (`mod` 2) [2, 1, 4, 3]
43 |
44 | test_groupOnSort :: IO ()
45 | test_groupOnSort =
46 | do assertEqual [] $ groupOnSort id ([] :: [()])
47 | assertEqual [((), [()])] $ groupOnSort id [()]
48 | assertEqual [((), [(), ()])] $ groupOnSort id [(), ()]
49 | assertEqual [(0, [2]), (1, [1])] $ groupOnSort (`mod` 2) [1, 2]
50 | assertEqual [(0, [2]), (1, [1])] $ groupOnSort (`mod` 2) [2, 1]
51 | assertEqual [(0, [2, 4]), (1, [1, 3])] $ groupOnSort (`mod` 2) [1, 2, 3, 4]
52 | assertEqual [(0, [2, 4]), (1, [1, 3])] $ groupOnSort (`mod` 2) [1, 2, 4, 3]
53 | assertEqual [(0, [2, 4]), (1, [1, 3])] $ groupOnSort (`mod` 2) [2, 1, 4, 3]
54 |
55 | test_headM :: IO ()
56 | test_headM =
57 | do assertEqual Nothing $ headM ([] :: [()])
58 | assertEqual (Just ()) $ headM [()]
59 | assertEqual (Just 1) $ headM [1,2]
60 |
61 | test_lastM :: IO ()
62 | test_lastM =
63 | do assertEqual Nothing $ lastM ([] :: [()])
64 | assertEqual (Just ()) $ lastM [()]
65 | assertEqual (Just 2) $ lastM [1,2]
66 |
67 | test_minimumM :: IO ()
68 | test_minimumM =
69 | do assertEqual Nothing $ minimumM ([] :: [Int])
70 | assertEqual (Just 1) $ minimumM [1]
71 | assertEqual (Just 1) $ minimumM [1,2]
72 | assertEqual (Just 1) $ minimumM [2,1]
73 | assertEqual (Just 1) $ minimumM [2,1,2]
74 |
75 | test_maximumM :: IO ()
76 | test_maximumM =
77 | do assertEqual Nothing $ maximumM ([] :: [Int])
78 | assertEqual (Just 2) $ maximumM [2]
79 | assertEqual (Just 2) $ maximumM [2,1]
80 | assertEqual (Just 2) $ maximumM [1,2]
81 | assertEqual (Just 2) $ maximumM [1,2,1]
82 |
83 | test_merge :: IO ()
84 | test_merge =
85 | do assertEqual [1, 2, 3, 4, 5] $ nubMerge as bs
86 | assertEqual [] $ nubMerge empty empty
87 | assertEqual [1] $ nubMerge one empty
88 | assertEqual [1] $ nubMerge empty one
89 | assertEqual [1, 2, 3] $ nubMerge cs cs
90 | assertEqual [1, 2, 3, 4, 5, 6] $ nubMerge cs ds
91 | assertEqual [1, 2, 3, 4, 5, 6] $ nubMerge es fs
92 | assertEqual [1] $ nubMerge two one
93 | assertEqual [1] $ nubMerge one two
94 | assertEqual [1] $ nubMerge three empty
95 | assertEqual [1] $ nubMerge empty three
96 |
97 | assertEqual [1, 2, 2, 3, 4, 4, 5] $ merge as bs
98 | assertEqual [] $ merge empty empty
99 | assertEqual [1] $ merge one empty
100 | assertEqual [1] $ merge empty one
101 | assertEqual [1, 1, 2, 2, 3, 3] $ merge cs cs
102 | assertEqual [1, 2, 3, 4, 5, 6] $ merge cs ds
103 | assertEqual [1, 2, 3, 4, 5, 6] $ merge es fs
104 | assertEqual [1, 1, 1] $ merge two one
105 | assertEqual [1, 1, 1] $ merge one two
106 | assertEqual [1, 1, 1] $ merge three empty
107 | assertEqual [1, 1, 1] $ merge empty three
108 | where
109 | empty, one, two, three, as, bs, cs, ds, es, fs :: [Int]
110 | as = [1, 2, 3, 4]
111 | bs = [2, 4, 5]
112 | cs = [1, 2, 3]
113 | ds = [4, 5, 6]
114 | es = [1, 3, 5]
115 | fs = [2, 4, 6]
116 | empty = []
117 | one = [1]
118 | two = [1, 1]
119 | three = [1, 1, 1]
120 |
121 |
122 | test_ungroup :: IO ()
123 | test_ungroup =
124 | assertEqual (Just [("a","1"),("a","2"),("a","3"),("b","4")])
125 | $ ungroupMay [("a",["1","2","3"]),("b",["4"])]
126 |
127 | test_ungroupGroup :: IO ()
128 | test_ungroupGroup =
129 | do let list = [("x","1"),("y","1"),("y","3"),("y","1")]
130 | assertEqual (Just list) (ungroupMay $ groupOn' id list)
131 |
132 | test_stripSuffix :: IO ()
133 | test_stripSuffix =
134 | do assertEqual (Just "foo") $ stripSuffix "bar" "foobar"
135 | assertEqual (Just "") $ stripSuffix "bar" "bar"
136 | assertEqual Nothing $ stripSuffix "bar" "foobars"
137 |
138 | test_monotone :: IO ()
139 | test_monotone =
140 | do assertEqual True $ monotone [1,2,3]
141 | assertEqual False $ monotone [-1,0,3,2]
142 | assertEqual True $ monotone [1]
143 | assertEqual True $ monotone ([] :: [Int])
144 |
145 | test_lastElems :: IO ()
146 | test_lastElems =
147 | do assertEqual ([]::[Int]) (lastElems 100 [])
148 | assertEqual [1,2,3] (lastElems 5 [1,2,3])
149 | assertEqual [1,2,3] (lastElems 3 [1,2,3])
150 | assertEqual [2,3] (lastElems 2 [1,2,3])
151 |
152 |
153 | prop_lastElems :: [Int] -> Int -> Bool
154 | prop_lastElems l n =
155 | lastElems n l `L.isSuffixOf` l
156 |
157 | test_makeMapping :: IO ()
158 | test_makeMapping =
159 | do assertEqual [] (makeMapping ([]::[(Int, String)]))
160 | let l = [(1::Int, "one"), (2, "two")] in assertEqual l (makeMapping l)
161 | assertEqual [(2::Int, "two"), (1, "three")]
162 | (makeMapping [(1, "one"), (2, "two"), (1, "three")])
163 | assertEqual [(1::Int, "x")] (makeMapping [(1,"x"),(1,"x")])
164 | let l2 = [(-2,""),(-2,"a"),(-2,"")]
165 | assertBool $ checkOrder (makeMapping l2) l2
166 |
167 | test_chunksOf :: IO ()
168 | test_chunksOf =
169 | do assertEqual [] (chunksOf 1 ([] :: [Int]))
170 | assertEqual [] (chunksOf 0 [1])
171 | assertEqual [[1], [2], [3], [4]] (chunksOf 1 [1, 2, 3, 4])
172 | assertEqual [[1, 2], [3, 4]] (chunksOf 2 [1, 2, 3, 4])
173 | assertEqual [[1, 2], [3]] (chunksOf 2 [1, 2, 3])
174 | assertEqual [[1, 2, 3]] (chunksOf 3 [1, 2, 3])
175 |
176 | test_prefixesAndSuffixes :: IO ()
177 | test_prefixesAndSuffixes =
178 | do assertEqual (prefixesAndSuffixes "") [("","")]
179 | assertEqual (prefixesAndSuffixes "Hallo")
180 | [("","Hallo"), ("H","allo"),("Ha","llo"),("Hal","lo"),("Hall","o"),("Hallo","")]
181 |
182 | prop_makeMappingConcat :: [(Int, String)] -> Bool
183 | prop_makeMappingConcat l =
184 | makeMapping l == makeMapping (l ++ l)
185 |
186 | prop_makeMappingKeysUnique :: [(Int, String)] -> Bool
187 | prop_makeMappingKeysUnique l =
188 | length (map fst (makeMapping l)) == Set.size (Set.fromList (map fst l))
189 |
190 | prop_makeMappingKeyValsOk :: [(Int, String)] -> Bool
191 | prop_makeMappingKeyValsOk l =
192 | Map.fromList (makeMapping l) == Map.fromList l
193 |
194 | prop_makeMappingOrderingOk :: [(Int, String)] -> Bool
195 | prop_makeMappingOrderingOk l =
196 | checkOrder (makeMapping l) l
197 |
198 | checkOrder :: [(Int, String)] -> [(Int, String)] -> Bool
199 | checkOrder [] [] = True
200 | checkOrder (x:xs) (y:ys)
201 | | x == y = checkOrder xs (dropWhile ((fst x ==) . fst) ys)
202 | | otherwise = checkOrder (x:xs) ys
203 | checkOrder _ _ = False
204 |
205 | test_withLast :: IO ()
206 | test_withLast =
207 | do assertEqual [] (withLast not [])
208 | assertEqual [False] (withLast not [True])
209 | assertEqual [True,False] (withLast not [True,True])
210 |
--------------------------------------------------------------------------------
/util-plus/test/GHC/Stack/PlusSpec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module GHC.Stack.PlusSpec
3 | ( htf_thisModulesTests
4 | )
5 | where
6 |
7 | import GHC.Stack.Plus
8 | import Test.Framework
9 |
10 | test_callerSrcLoc :: IO ()
11 | test_callerSrcLoc =
12 | do assertEqual __FILE__ callerFile
13 | assertEqual __LINE__ callerLine
14 |
--------------------------------------------------------------------------------
/util-plus/test/Safe/PlusSpec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Safe.PlusSpec where
3 |
4 | import GHC.Exception
5 | import Safe.Plus
6 | import Test.Framework
7 |
8 | test_safeError :: IO ()
9 | test_safeError =
10 | assertThrows (safeError msg) (matches (format __FILE__ __LINE__))
11 | where
12 | matches expected (ErrorCallWithLocation actual _) = expected == actual
13 | format :: String -> Int -> String
14 | format file line = file ++ ":" ++ show line ++ ": " ++ msg
15 | msg = "error message"
16 |
--------------------------------------------------------------------------------
/util-plus/test/Spec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF htfpp #-}
2 | module Main where
3 |
4 | import {-@ HTF_TESTS @-} Data.List.PlusSpec
5 | import {-@ HTF_TESTS @-} GHC.Stack.PlusSpec
6 | import {-@ HTF_TESTS @-} Safe.PlusSpec
7 |
8 | import Test.Framework
9 |
10 | main :: IO ()
11 | main = htfMain htf_importedTests
12 |
--------------------------------------------------------------------------------
/util-plus/util-plus.cabal:
--------------------------------------------------------------------------------
1 | name: util-plus
2 | version: 0.1.0.0
3 | synopsis: A collection of commonly used utils
4 | description: A collection of commonly used util functions for basic libaries
5 | homepage: https://github.com/factisresearch/opensource#readme
6 | license: BSD3
7 | license-file: LICENSE
8 | author: factis research GmbH
9 | maintainer: mail@athiemann.net
10 | copyright: 2017 factis research GmbH
11 | category: Data
12 | build-type: Simple
13 | extra-source-files:
14 | README.md
15 | cabal-version: >=1.10
16 |
17 | library
18 | hs-source-dirs: src
19 | exposed-modules: GHC.Stack.Plus
20 | , Safe.Plus
21 | , Data.List.Plus
22 | , Control.Applicative.Plus
23 | build-depends: base >= 4.7 && < 5
24 | , safe
25 | , array
26 | , hashable
27 | , containers
28 | , unordered-containers
29 | default-language: Haskell2010
30 | ghc-options: -Wall
31 |
32 | test-suite util-plus-test
33 | type: exitcode-stdio-1.0
34 | hs-source-dirs: test
35 | main-is: Spec.hs
36 | other-modules: GHC.Stack.PlusSpec
37 | , Safe.PlusSpec
38 | , Data.List.PlusSpec
39 | build-depends: base >= 4.7 && < 5
40 | , HTF
41 | , QuickCheck
42 | , containers
43 | , util-plus
44 | ghc-options: -Wall
45 | default-language: Haskell2010
46 |
47 | source-repository head
48 | type: git
49 | location: https://github.com/factisresearch/opensource.git
50 |
--------------------------------------------------------------------------------