Skip to content

Commit afcbc77

Browse files
authored
Improve HashSet API docs. (#267)
- More information in introduction (basic operations, and using HashSet with custom data types) - Examples alongside function docs
1 parent 7485f5c commit afcbc77

File tree

2 files changed

+143
-30
lines changed

2 files changed

+143
-30
lines changed

Data/HashSet.hs

+88-19
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,94 @@
44
#endif
55

66
------------------------------------------------------------------------
7-
-- |
8-
-- Module : Data.HashSet
9-
-- Copyright : 2011 Bryan O'Sullivan
10-
-- License : BSD-style
11-
-- Maintainer : [email protected]
12-
-- Stability : provisional
13-
-- Portability : portable
14-
--
15-
-- A set of /hashable/ values. A set cannot contain duplicate items.
16-
-- A 'HashSet' makes no guarantees as to the order of its elements.
17-
--
18-
-- The implementation is based on /hash array mapped trie/. A
19-
-- 'HashSet' is often faster than other tree-based set types,
20-
-- especially when value comparison is expensive, as in the case of
21-
-- strings.
22-
--
23-
-- Many operations have a average-case complexity of /O(log n)/. The
24-
-- implementation uses a large base (i.e. 16) so in practice these
25-
-- operations are constant time.
7+
{-|
8+
Module : Data.HashSet
9+
Copyright : 2011 Bryan O'Sullivan
10+
License : BSD-style
11+
Maintainer : [email protected]
12+
Stability : provisional
13+
Portability : portable
14+
15+
= Introduction
16+
17+
'HashSet' allows you to store /unique/ elements, providing efficient insertion,
18+
lookups, and deletion. A 'HashSet' makes no guarantees as to the order of its
19+
elements.
20+
21+
If you are storing sets of "Data.Int"s consider using "Data.IntSet" from the
22+
<https://hackage.haskell.org/package/containers containers> package.
23+
24+
25+
== Examples
26+
27+
All the examples below assume @HashSet@ is imported qualified, and uses the following @dataStructures@ set.
28+
29+
>>> import qualified Data.HashSet as HashSet
30+
>>> let dataStructures = HashSet.fromList ["Set", "Map", "Graph", "Sequence"]
31+
32+
=== Basic Operations
33+
34+
Check membership in a set:
35+
36+
>>> -- Check if "Map" and "Trie" are in the set of data structures.
37+
>>> HashSet.member "Map" dataStructures
38+
True
39+
>>> HashSet.member "Trie" dataStructures
40+
False
41+
42+
Add a new entry to the set:
43+
44+
>>> let moreDataStructures = HashSet.insert "Trie" dataStructures
45+
>>> HashSet.member "Trie" moreDataStructures
46+
> True
47+
48+
Remove the @\"Graph\"@ entry from the set of data structures.
49+
50+
>>> let fewerDataStructures = HashSet.delete "Graph" dataStructures
51+
>>> HashSet.toList fewerDataStructures
52+
["Map","Set","Sequence"]
53+
54+
55+
Create a new set and combine it with our original set.
56+
57+
>>> let unorderedDataStructures = HashSet.fromList ["HashSet", "HashMap"]
58+
>>> HashSet.union dataStructures unorderedDataStructures
59+
fromList ["Map","HashSet","Graph","HashMap","Set","Sequence"]
60+
61+
=== Using custom data with HashSet
62+
63+
To create a @HashSet@ of your custom type, the type must have instances for
64+
'Data.Eq.Eq' and 'Data.Hashable.Hashable'. The @Hashable@ typeclass is defined in the
65+
<https://hackage.haskell.org/package/hashable hashable> package, see the
66+
documentation for information on how to make your type an instance of
67+
@Hashable@.
68+
69+
We'll start by setting up our custom data type:
70+
71+
>>> :set -XDeriveGeneric
72+
>>> import GHC.Generics (Generic)
73+
>>> import Data.Hashable
74+
>>> data Person = Person { name :: String, likesDogs :: Bool } deriving (Show, Eq, Generic)
75+
>>> instance Hashable Person
76+
77+
And now we'll use it!
78+
79+
>>> let people = HashSet.fromList [Person "Lana" True, Person "Joe" False, Person "Simon" True]
80+
>>> HashSet.filter likesDogs people
81+
fromList [Person {name = "Simon", likesDogs = True},Person {name = "Lana", likesDogs = True}]
82+
83+
84+
== Performance
85+
86+
The implementation is based on /hash array mapped tries/. A
87+
'HashSet' is often faster than other 'Data.Ord.Ord'-based set types,
88+
especially when value comparisons are expensive, as in the case of
89+
strings.
90+
91+
Many operations have a average-case complexity of /O(log n)/. The
92+
implementation uses a large base (i.e. 16) so in practice these
93+
operations are constant time.
94+
-}
2695

2796
module Data.HashSet
2897
(

Data/HashSet/Base.hs

+55-11
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
-- A set of /hashable/ values. A set cannot contain duplicate items.
2020
-- A 'HashSet' makes no guarantees as to the order of its elements.
2121
--
22-
-- The implementation is based on /hash array mapped trie/. A
22+
-- The implementation is based on /hash array mapped tries/. A
2323
-- 'HashSet' is often faster than other tree-based set types,
2424
-- especially when value comparison is expensive, as in the case of
2525
-- strings.
@@ -36,10 +36,6 @@ module Data.HashSet.Base
3636
, empty
3737
, singleton
3838

39-
-- * Combine
40-
, union
41-
, unions
42-
4339
-- * Basic interface
4440
, null
4541
, size
@@ -50,6 +46,10 @@ module Data.HashSet.Base
5046
-- * Transformations
5147
, map
5248

49+
-- * Combine
50+
, union
51+
, unions
52+
5353
-- * Difference and intersection
5454
, difference
5555
, intersection
@@ -260,24 +260,39 @@ hashSetDataType :: DataType
260260
hashSetDataType = mkDataType "Data.HashSet.Base.HashSet" [fromListConstr]
261261

262262
-- | /O(1)/ Construct an empty set.
263+
--
264+
-- >>> HashSet.empty
265+
-- fromList []
263266
empty :: HashSet a
264267
empty = HashSet H.empty
265268

266269
-- | /O(1)/ Construct a set with a single element.
270+
--
271+
-- >>> HashSet.singleton 1
272+
-- fromList [1]
267273
singleton :: Hashable a => a -> HashSet a
268274
singleton a = HashSet (H.singleton a ())
269275
{-# INLINABLE singleton #-}
270276

271-
-- | /O(1)/ Convert to the equivalent 'HashMap'.
277+
-- | /O(1)/ Convert to set to the equivalent 'HashMap' with @()@ values.
278+
--
279+
-- >>> HashSet.toMap (HashSet.singleton 1)
280+
-- fromList [(1,())]
272281
toMap :: HashSet a -> HashMap a ()
273282
toMap = asMap
274283

275-
-- | /O(1)/ Convert from the equivalent 'HashMap'.
284+
-- | /O(1)/ Convert from the equivalent 'HashMap' with @()@ values.
285+
--
286+
-- >>> HashSet.fromMap (HashMap.singleton 1 ())
287+
-- fromList [1]
276288
fromMap :: HashMap a () -> HashSet a
277289
fromMap = HashSet
278290

279291
-- | /O(n)/ Produce a 'HashSet' of all the keys in the given 'HashMap'.
280292
--
293+
-- >>> HashSet.keysSet (HashMap.fromList [(1, "a"), (2, "b")]
294+
-- fromList [1,2]
295+
--
281296
-- @since 0.2.10.0
282297
keysSet :: HashMap k a -> HashSet k
283298
keysSet m = fromMap (() <$ m)
@@ -287,8 +302,6 @@ keysSet m = fromMap (() <$ m)
287302
-- To obtain good performance, the smaller set must be presented as
288303
-- the first argument.
289304
--
290-
-- ==== __Examples__
291-
--
292305
-- >>> union (fromList [1,2]) (fromList [2,3])
293306
-- fromList [1,2,3]
294307
union :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
@@ -303,48 +316,79 @@ unions = List.foldl' union empty
303316
{-# INLINE unions #-}
304317

305318
-- | /O(1)/ Return 'True' if this set is empty, 'False' otherwise.
319+
--
320+
-- >>> HashSet.null HashSet.empty
321+
-- True
322+
-- >>> HashSet.null (HashSet.singleton 1)
323+
-- False
306324
null :: HashSet a -> Bool
307325
null = H.null . asMap
308326
{-# INLINE null #-}
309327

310328
-- | /O(n)/ Return the number of elements in this set.
329+
--
330+
-- >>> HashSet.size HashSet.empty
331+
-- 0
332+
-- >>> HashSet.size (HashSet.fromList [1,2,3])
333+
-- 3
311334
size :: HashSet a -> Int
312335
size = H.size . asMap
313336
{-# INLINE size #-}
314337

315338
-- | /O(log n)/ Return 'True' if the given value is present in this
316339
-- set, 'False' otherwise.
340+
--
341+
-- >>> HashSet.member 1 (Hashset.fromList [1,2,3])
342+
-- True
343+
-- >>> HashSet.member 1 (Hashset.fromList [4,5,6])
344+
-- False
317345
member :: (Eq a, Hashable a) => a -> HashSet a -> Bool
318346
member a s = case H.lookup a (asMap s) of
319347
Just _ -> True
320348
_ -> False
321349
{-# INLINABLE member #-}
322350

323351
-- | /O(log n)/ Add the specified value to this set.
352+
--
353+
-- >>> HashSet.insert 1 HashSet.empty
354+
-- fromList [1]
324355
insert :: (Eq a, Hashable a) => a -> HashSet a -> HashSet a
325356
insert a = HashSet . H.insert a () . asMap
326357
{-# INLINABLE insert #-}
327358

328-
-- | /O(log n)/ Remove the specified value from this set if
329-
-- present.
359+
-- | /O(log n)/ Remove the specified value from this set if present.
360+
--
361+
-- >>> HashSet.delete 1 (HashSet.fromList [1,2,3])
362+
-- fromList [2,3]
363+
-- >>> HashSet.delete 1 (HashSet.fromList [4,5,6])
364+
-- fromList [4,5,6]
330365
delete :: (Eq a, Hashable a) => a -> HashSet a -> HashSet a
331366
delete a = HashSet . H.delete a . asMap
332367
{-# INLINABLE delete #-}
333368

334369
-- | /O(n)/ Transform this set by applying a function to every value.
335370
-- The resulting set may be smaller than the source.
371+
--
372+
-- >>> HashSet.map show (HashSet.fromList [1,2,3])
373+
-- HashSet.fromList ["1","2","3"]
336374
map :: (Hashable b, Eq b) => (a -> b) -> HashSet a -> HashSet b
337375
map f = fromList . List.map f . toList
338376
{-# INLINE map #-}
339377

340378
-- | /O(n)/ Difference of two sets. Return elements of the first set
341379
-- not existing in the second.
380+
--
381+
-- >>> HashSet.difference (HashSet.fromList [1,2,3]) (HashSet.fromList [2,3,4])
382+
-- fromList [1]
342383
difference :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
343384
difference (HashSet a) (HashSet b) = HashSet (H.difference a b)
344385
{-# INLINABLE difference #-}
345386

346387
-- | /O(n)/ Intersection of two sets. Return elements present in both
347388
-- the first set and the second.
389+
--
390+
-- >>> HashSet.intersection (HashSet.fromList [1,2,3]) (HashSet.fromList [2,3,4])
391+
-- fromList [2,3]
348392
intersection :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
349393
intersection (HashSet a) (HashSet b) = HashSet (H.intersection a b)
350394
{-# INLINABLE intersection #-}

0 commit comments

Comments
 (0)