Skip to content

Drop Semigroup/Monoid instances for Map; add SemigroupMap #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 45 commits into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
21b21a1
Copy Internal, Gen, and main Map module to new Unbiased folder
JordanMartinez Dec 20, 2020
e6820c1
Add 'Unbiased' to new modules
JordanMartinez Dec 20, 2020
77e3802
Unbias the Map's Semigroup instance
JordanMartinez Dec 20, 2020
12ca79d
Update instance name to match conventions
JordanMartinez Dec 20, 2020
c7dc864
Add Alt instance to unbiased Map
JordanMartinez Dec 20, 2020
ffdf000
Duplicate biased Map's test into Unbiased file
JordanMartinez Dec 20, 2020
c2ec2a2
Update unbiased test to use unbiased Map
JordanMartinez Dec 20, 2020
9f1c384
Fix Alt instance
JordanMartinez Dec 20, 2020
43adba9
Export the unbiased map internal module
JordanMartinez Dec 21, 2020
868eb71
Convert unbiased Map to newtype
JordanMartinez Dec 21, 2020
732af66
Fix Arbitrary instances
JordanMartinez Dec 23, 2020
7e5b11a
Replace specialized A.all with generic all
JordanMartinez Dec 23, 2020
7c2788a
Add Semigroup tests to both Maps
JordanMartinez Dec 23, 2020
c7ca250
Include unbiased map tests in repo's tests
JordanMartinez Dec 23, 2020
e5bfeb6
Remove all non-typeclass-instance code from Unbiased Map
JordanMartinez Dec 23, 2020
01232e1
Add newtype instance to Unbiased Map
JordanMartinez Dec 23, 2020
ef7ef66
Remove unused import
JordanMartinez Dec 23, 2020
1e66c06
Merge remote-tracking branch 'upstream/master' into addUnbiasedMap
JordanMartinez Dec 23, 2020
7b59b05
Add Apply and Bind instances to Unbiased Map to match Map
JordanMartinez Dec 23, 2020
5bb69aa
Remove Semigroup and Monoid instances for normal Map
JordanMartinez Dec 23, 2020
95700f3
Add Alt instance to normal Map and derive it for Unbiased Map
JordanMartinez Dec 23, 2020
79cd3e3
No longer derive unbiased Map's Monoid instance
JordanMartinez Dec 23, 2020
f485637
Remove normal Map's Semigroup instance test
JordanMartinez Dec 23, 2020
d70951e
Reimplement submap without depending on Monoid instance
JordanMartinez Dec 23, 2020
e3d7d07
Remove "\" character that I somehow inserted
JordanMartinez Dec 23, 2020
618c922
Update unbiased Map's Semigroup instance name to match naming convent…
JordanMartinez Dec 23, 2020
169152c
Move asList past type class instances
JordanMartinez Dec 23, 2020
e39ae7e
Move SemigroupMap (aka the unbiased Map) into Data.Map; update tests
JordanMartinez Dec 23, 2020
cca21c6
Merge remote-tracking branch 'upstream/master' into addUnbiasedMap
JordanMartinez Dec 23, 2020
6a74675
Remove unused safe coerce as a dependency
JordanMartinez Dec 24, 2020
87ebc7b
Add Plus instance to Map
JordanMartinez Dec 24, 2020
8e86d2e
Remove role annotations for SemigroupMap; it will use Map's role anno…
JordanMartinez Dec 24, 2020
cee5474
Use long-form names for SemigroupMap's type parameters in docs
JordanMartinez Dec 24, 2020
985a076
Fix typo in docs: Last 1 should be Last 2
JordanMartinez Dec 24, 2020
793c52b
Remove module prefixes in SemigroupMap docs; types are in same module
JordanMartinez Dec 24, 2020
5ef6eb7
Update Semigroup docs to use `k v` rather than `key value`
JordanMartinez Dec 24, 2020
c266d77
Derive Show instance for SemigroupMap
JordanMartinez Dec 25, 2020
134ce5e
Move SemigroupMap from Internal module to Data.Map
JordanMartinez Jan 26, 2021
db87c5d
Update CI to 0.14.0-rc5
JordanMartinez Jan 26, 2021
4be58ec
Fix imports due to relocating SemigroupMap
JordanMartinez Jan 26, 2021
b0bfa20
Fix NonEmptySet Foldable1 instance
JordanMartinez Jan 26, 2021
ec42bd5
Fix tests that use `nubBy`
JordanMartinez Jan 26, 2021
e277da4
Merge remote-tracking branch 'upstream/master' into addUnbiasedMap
JordanMartinez Jan 26, 2021
aa75462
Add this PR to the changelog
JordanMartinez Jan 26, 2021
694fa0f
Update changelog: added Alt and Plus instances to Map
JordanMartinez Jan 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ Notable changes to this project are documented in this file. The format is based

Breaking changes:
- Added support for PureScript 0.14 and dropped support for all previous versions (#35, #43)
- Drop `Map`'s `Semigroup` and `Monoid` instances and provide unbiased instances via `SemigroupMap` newtype (#38)

New features:
- Added `Apply` instance for `Map` (#16)
- Added `Alt` and `Plus` instances for `Map` (#38)
- Added `catMaybes` for maps and sets (#25)
- Added `toMap` and `fromMap` to `Data.Set` (#31)

Expand Down
51 changes: 51 additions & 0 deletions src/Data/Map.purs
Original file line number Diff line number Diff line change
@@ -1,14 +1,65 @@
module Data.Map
( module Data.Map.Internal
, keys
, SemigroupMap(..)
) where

import Prelude

import Control.Alt (class Alt)
import Control.Plus (class Plus)
import Data.Eq (class Eq1)
import Data.Foldable (class Foldable)
import Data.FoldableWithIndex (class FoldableWithIndex)
import Data.FunctorWithIndex (class FunctorWithIndex)
import Data.Map.Internal (Map, alter, catMaybes, checkValid, delete, empty, filter, filterKeys, filterWithKey, findMax, findMin, foldSubmap, fromFoldable, fromFoldableWith, fromFoldableWithIndex, insert, insertWith, isEmpty, isSubmap, lookup, lookupGE, lookupGT, lookupLE, lookupLT, member, pop, showTree, singleton, size, submap, toUnfoldable, toUnfoldableUnordered, union, unionWith, unions, intersection, intersectionWith, difference, update, values, mapMaybeWithKey, mapMaybe)
import Data.Newtype (class Newtype)
import Data.Ord (class Ord1)
import Data.Traversable (class Traversable)
import Data.TraversableWithIndex (class TraversableWithIndex)
import Data.Set (Set, fromMap)

-- | The set of keys of the given map.
-- | See also `Data.Set.fromMap`.
keys :: forall k v. Map k v -> Set k
keys = fromMap <<< void

-- | `SemigroupMap k v` provides a `Semigroup` instance for `Map k v` whose
-- | definition depends on the `Semigroup` instance for the `v` type.
-- | You should only use this type when you need `Data.Map` to have
-- | a `Semigroup` instance.
-- |
-- | ```purescript
-- | let
-- | s :: forall key value. key -> value -> SemigroupMap key value
-- | s k v = SemigroupMap (singleton k v)
-- |
-- | (s 1 "foo") <> (s 1 "bar") == (s 1 "foobar")
-- | (s 1 (First 1)) <> (s 1 (First 2)) == (s 1 (First 1))
-- | (s 1 (Last 1)) <> (s 1 (Last 2)) == (s 1 (Last 2))
-- | ```
newtype SemigroupMap k v = SemigroupMap (Map k v)

derive newtype instance eq1SemigroupMap :: Eq k => Eq1 (SemigroupMap k)
derive newtype instance eqSemigroupMap :: (Eq k, Eq v) => Eq (SemigroupMap k v)
derive newtype instance ord1SemigroupMap :: Ord k => Ord1 (SemigroupMap k)
derive newtype instance ordSemigroupMap :: (Ord k, Ord v) => Ord (SemigroupMap k v)
derive instance newtypeSemigroupMap :: Newtype (SemigroupMap k v) _
derive newtype instance showSemigroupMap :: (Show k, Show v) => Show (SemigroupMap k v)

instance semigroupSemigroupMap :: (Ord k, Semigroup v) => Semigroup (SemigroupMap k v) where
append (SemigroupMap l) (SemigroupMap r) = SemigroupMap (unionWith append l r)

instance monoidSemigroupMap :: (Ord k, Semigroup v) => Monoid (SemigroupMap k v) where
mempty = SemigroupMap empty

derive newtype instance altSemigroupMap :: Ord k => Alt (SemigroupMap k)
derive newtype instance plusSemigroupMap :: Ord k => Plus (SemigroupMap k)
derive newtype instance functorSemigroupMap :: Functor (SemigroupMap k)
derive newtype instance functorWithIndexSemigroupMap :: FunctorWithIndex k (SemigroupMap k)
derive newtype instance applySemigroupMap :: Ord k => Apply (SemigroupMap k)
derive newtype instance bindSemigroupMap :: Ord k => Bind (SemigroupMap k)
derive newtype instance foldableSemigroupMap :: Foldable (SemigroupMap k)
derive newtype instance foldableWithIndexSemigroupMap :: FoldableWithIndex k (SemigroupMap k)
derive newtype instance traversableSemigroupMap :: Traversable (SemigroupMap k)
derive newtype instance traversableWithIndexSemigroupMap :: TraversableWithIndex k (SemigroupMap k)
41 changes: 23 additions & 18 deletions src/Data/Map/Internal.purs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ module Data.Map.Internal

import Prelude

import Control.Alt (class Alt)
import Control.Plus (class Plus)
import Data.Eq (class Eq1)
import Data.Foldable (foldl, foldMap, foldr, class Foldable)
import Data.FoldableWithIndex (class FoldableWithIndex, foldlWithIndex, foldrWithIndex)
Expand Down Expand Up @@ -90,11 +92,11 @@ instance ordMap :: (Ord k, Ord v) => Ord (Map k v) where
instance showMap :: (Show k, Show v) => Show (Map k v) where
show m = "(fromFoldable " <> show (toAscArray m) <> ")"

instance semigroupMap :: Ord k => Semigroup (Map k v) where
append = union
instance altMap :: Ord k => Alt (Map k) where
alt = union

instance monoidMap :: Ord k => Monoid (Map k v) where
mempty = empty
instance plusMap :: Ord k => Plus (Map k) where
empty = empty

instance functorMap :: Functor (Map k) where
map _ Leaf = Leaf
Expand Down Expand Up @@ -122,9 +124,6 @@ instance foldableWithIndexMap :: FoldableWithIndex k (Map k) where
foldrWithIndex f z m = foldr (uncurry f) z $ asList $ toUnfoldable m
foldMapWithIndex f m = foldMap (uncurry f) $ asList $ toUnfoldable m

asList :: forall k v. List (Tuple k v) -> List (Tuple k v)
asList = identity

instance traversableMap :: Traversable (Map k) where
traverse f Leaf = pure Leaf
traverse f (Two left k v right) =
Expand Down Expand Up @@ -158,6 +157,9 @@ instance traversableWithIndexMap :: TraversableWithIndex k (Map k) where
<*> f k2 v2
<*> traverseWithIndex f right

asList :: forall k v. List (Tuple k v) -> List (Tuple k v)
asList = identity

-- | Render a `Map` as a `String`
showTree :: forall k v. Show k => Show v => Map k v -> String
showTree Leaf = "Leaf"
Expand Down Expand Up @@ -322,7 +324,10 @@ findMin = go Nothing
-- | == ["zero", "one", "two"]
-- | ```
foldSubmap :: forall k v m. Ord k => Monoid m => Maybe k -> Maybe k -> (k -> v -> m) -> Map k v -> m
foldSubmap kmin kmax f =
foldSubmap = foldSubmapBy (<>) mempty

foldSubmapBy :: forall k v m. Ord k => (m -> m -> m) -> m -> Maybe k -> Maybe k -> (k -> v -> m) -> Map k v -> m
foldSubmapBy appendFn memptyValue kmin kmax f =
let
tooSmall =
case kmin of
Expand Down Expand Up @@ -367,17 +372,17 @@ foldSubmap kmin kmax f =
-- function because of strictness.
go = case _ of
Leaf ->
mempty
memptyValue
Two left k v right ->
(if tooSmall k then mempty else go left)
<> (if inBounds k then f k v else mempty)
<> (if tooLarge k then mempty else go right)
(if tooSmall k then memptyValue else go left)
`appendFn` (if inBounds k then f k v else memptyValue)
`appendFn` (if tooLarge k then memptyValue else go right)
Three left k1 v1 mid k2 v2 right ->
(if tooSmall k1 then mempty else go left)
<> (if inBounds k1 then f k1 v1 else mempty)
<> (if tooSmall k2 || tooLarge k1 then mempty else go mid)
<> (if inBounds k2 then f k2 v2 else mempty)
<> (if tooLarge k2 then mempty else go right)
(if tooSmall k1 then memptyValue else go left)
`appendFn` (if inBounds k1 then f k1 v1 else memptyValue)
`appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid)
`appendFn` (if inBounds k2 then f k2 v2 else memptyValue)
`appendFn` (if tooLarge k2 then memptyValue else go right)
in
go

Expand Down Expand Up @@ -408,7 +413,7 @@ foldSubmap kmin kmax f =
-- | else not (member key m')
-- | ```
submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v
submap kmin kmax = foldSubmap kmin kmax singleton
submap kmin kmax = foldSubmapBy union empty kmin kmax singleton

-- | Test if a key is a member of a map
member :: forall k v. Ord k => k -> Map k v -> Boolean
Expand Down
30 changes: 28 additions & 2 deletions test/Test/Data/Map.purs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import Data.List.NonEmpty as NEL
import Data.Map as M
import Data.Map.Gen (genMap)
import Data.Maybe (Maybe(..), fromMaybe, maybe)
import Data.Semigroup.First (First(..))
import Data.Semigroup.Last (Last(..))
import Data.Tuple (Tuple(..), fst, uncurry)
import Effect (Effect)
import Effect.Console (log)
Expand Down Expand Up @@ -162,7 +164,7 @@ mapTests = do

log "sort . toUnfoldable . fromFoldable = sort (on lists without key-duplicates)"
quickCheck $ \(list :: List (Tuple SmallKey Int)) ->
let nubbedList = nubBy ((==) `on` fst) list
let nubbedList = nubBy (compare `on` fst) list
f x = M.toUnfoldable (M.fromFoldable x)
in sort (f nubbedList) == sort nubbedList <?> show nubbedList

Expand Down Expand Up @@ -254,7 +256,7 @@ mapTests = do

log "size"
quickCheck $ \xs ->
let xs' = nubBy ((==) `on` fst) xs
let xs' = nubBy (compare `on` fst) xs
in M.size (M.fromFoldable xs') == length (xs' :: List (Tuple SmallKey Int))

log "lookupLE result is correct"
Expand Down Expand Up @@ -399,3 +401,27 @@ mapTests = do
let result = M.catMaybes maybeMap
let expected = M.delete 1 m
result === expected

log "SemigroupMap's Semigroup instance is based on value's Semigroup instance"
quickCheck \(Tuple leftStr rightStr :: Tuple String String) -> do
let key = "foo"
let left = smSingleton key leftStr
let right = smSingleton key rightStr
let result = left <> right
let expected = smSingleton key $ leftStr <> rightStr
result == expected
quickCheck \(Tuple leftStr rightStr :: Tuple String String) -> do
let key = "foo"
let left = smSingleton key $ First leftStr
let right = smSingleton key $ First rightStr
let result = left <> right
result == left
quickCheck \(Tuple leftStr rightStr :: Tuple String String) -> do
let key = "foo"
let left = smSingleton key $ Last leftStr
let right = smSingleton key $ Last rightStr
let result = left <> right
result == right

smSingleton :: forall key value. key -> value -> M.SemigroupMap key value
smSingleton k v = M.SemigroupMap (M.singleton k v)