Add operator IntMap.!?. (#454)
authorMatt Renaud <matt@m-renaud.com>
Wed, 20 Dec 2017 22:56:24 +0000 (14:56 -0800)
committerDavid Feuer <David.Feuer@gmail.com>
Wed, 20 Dec 2017 22:56:24 +0000 (17:56 -0500)
Add operator `IntMap.!?`.

This operator was added to `Data.Map` but was accidentally left out of the `IntMap`
API.

```haskell
(!?) :: IntMap a -> Key -> Maybe a
```

Fixes #453

* Add missing import of operator (!?) from internal module.

* Add operator !? property tests to test list.

* Fix precedence issue in properties test.

* Add @since 0.5.11 annotation to Data.IntMap.!?.

* Use Prelude.map instead of <$> in intmap-properties.

<$> was not introduced into base until GHC 7.10 so integration tests run against
earlier compilers fail[1]. We go with Prelude.map since it is the simplest way
to be compatible with all GHC versions.

Alternatives considered:
  - import Data.Functor ((<$>)) or fmap
    - causes unused import warnings for GHC versions with <$> in base.
  - conditionally import <$>
    - Unnecessary noise in compile list.

[1]
https://travis-ci.org/haskell/containers/builds/318987347?utm_source=github_status&utm_medium=notification

Data/IntMap/Internal.hs
Data/IntMap/Lazy.hs
Data/IntMap/Strict.hs
tests/intmap-properties.hs
tests/intmap-strictness.hs

index 889ac82..c0b7570 100644 (file)
@@ -75,7 +75,7 @@ module Data.IntMap.Internal (
       IntMap(..), Key          -- instance Eq,Show
 
     -- * Operators
-    , (!), (\\)
+    , (!), (!?), (\\)
 
     -- * Query
     , null
@@ -382,11 +382,22 @@ bitmapOf x = shiftLL 1 (x .&. IntSet.suffixBitMask)
 (!) :: IntMap a -> Key -> a
 (!) m k = find k m
 
+-- | /O(min(n,W))/. Find the value at a key.
+-- Returns 'Nothing' when the element can not be found.
+--
+-- > fromList [(5,'a'), (3,'b')] !? 1 == Nothing
+-- > fromList [(5,'a'), (3,'b')] !? 5 == Just 'a'
+--
+-- @since 0.5.11
+
+(!?) :: IntMap a -> Key -> Maybe a
+(!?) m k = lookup k m
+
 -- | Same as 'difference'.
 (\\) :: IntMap a -> IntMap b -> IntMap a
 m1 \\ m2 = difference m1 m2
 
-infixl 9 \\{-This comment teaches CPP correct behaviour -}
+infixl 9 !?,\\{-This comment teaches CPP correct behaviour -}
 
 {--------------------------------------------------------------------
   Types
@@ -2170,7 +2181,7 @@ findMin :: IntMap a -> (Key, a)
 findMin t
   | Just r <- lookupMin t = r
   | otherwise = error "findMin: empty map has no minimal element"
-  
+
 -- | /O(min(n,W))/. The maximal key of the map. Returns 'Nothing' if the map is empty.
 lookupMax :: IntMap a -> Maybe (Key, a)
 lookupMax Nil = Nothing
@@ -2186,7 +2197,7 @@ lookupMax (Bin _ m l r)
 findMax :: IntMap a -> (Key, a)
 findMax t
   | Just r <- lookupMax t = r
-  | otherwise = error "findMax: empty map has no maximal element"          
+  | otherwise = error "findMax: empty map has no maximal element"
 
 -- | /O(min(n,W))/. Delete the minimal key. Returns an empty map if the map is empty.
 --
index fe558d8..ebd579c 100644 (file)
@@ -63,7 +63,7 @@ module Data.IntMap.Lazy (
 #endif
 
     -- * Operators
-    , (!), (\\)
+    , (!), (!?), (\\)
 
     -- * Query
     , IM.null
index af17784..dc638f0 100644 (file)
@@ -70,7 +70,7 @@ module Data.IntMap.Strict (
 #endif
 
     -- * Operators
-    , (!), (\\)
+    , (!), (!?), (\\)
 
     -- * Query
     , null
@@ -239,6 +239,7 @@ import Data.IntMap.Internal
 
   , (\\)
   , (!)
+  , (!?)
   , empty
   , assocs
   , filter
index 44464c5..3875ec6 100644 (file)
@@ -31,6 +31,7 @@ main :: IO ()
 main = defaultMain
          [
                testCase "index"      test_index
+             , testCase "index_lookup" test_index_lookup
              , testCase "size"       test_size
              , testCase "size2"      test_size2
              , testCase "member"     test_member
@@ -143,6 +144,7 @@ main = defaultMain
              , testProperty "fromList"             prop_fromList
              , testProperty "alter"                prop_alter
              , testProperty "index"                prop_index
+             , testProperty "index_lookup"         prop_index_lookup
              , testProperty "null"                 prop_null
              , testProperty "size"                 prop_size
              , testProperty "member"               prop_member
@@ -226,6 +228,11 @@ tests = [ testGroup "Test Case" [
 test_index :: Assertion
 test_index = fromList [(5,'a'), (3,'b')] ! 5 @?= 'a'
 
+test_index_lookup :: Assertion
+test_index_lookup = do
+    fromList [(5,'a'), (3,'b')] !? 1 @?= Nothing
+    fromList [(5,'a'), (3,'b')] !? 5 @?= Just 'a'
+
 ----------------------------------------------------------------
 -- Query
 
@@ -923,6 +930,11 @@ prop_index xs = length xs > 0 ==>
   let m  = fromList (zip xs xs)
   in  xs == [ m ! i | i <- xs ]
 
+prop_index_lookup :: [Int] -> Property
+prop_index_lookup xs = length xs > 0 ==>
+  let m  = fromList (zip xs xs)
+  in  (Prelude.map Just xs) == [ m !? i | i <- xs ]
+
 prop_null :: IMap -> Bool
 prop_null m = null m == (size m == 0)
 
index bc7c442..735b181 100644 (file)
@@ -91,6 +91,7 @@ tests =
       , testProperty "findWithDefault is key-strict" pFindWithDefaultKeyStrict
       , testProperty "findWithDefault is value-strict" pFindWithDefaultValueStrict
       , testProperty "! is key-strict" $ keyStrict (flip (M.!))
+      , testProperty "!? is key-strict" $ keyStrict (flip (M.!?))
       , testProperty "delete is key-strict" $ keyStrict M.delete
       , testProperty "adjust is key-strict" pAdjustKeyStrict
       , testProperty "adjust is value-strict" pAdjustValueStrict