Commits

Doug Burke committed 12fad56

LookupMap changes

Comments (0)

Files changed (9)

     (Swish.GraphMatch.LabelIndex) and in the bnode counts when formatting
     to N3/Turtle.
 
+  - Minor clean up of the LookupMap module: mergeReplaceOrAdd and mergeReplace
+    are now combined into mergeReplace; mapSelect has been removed; documentation
+    slightly improved, and a few minor internal clean ups.
+
   - Bump the upper constraint on the containers package to include
     version 0.5.
 
 
 - Look at using an interned URI in Namespace.
 
+- Can LookupMap be replaced by Map? This would require removing the
+  LookupEntryClass constraint which could cause troublesome.
+
 - Rename modules. Version 0.7 provided a hopefully more-structured
   form but they are still in the Swish namespace rather than
   Data (or some other sanctioned top-level name).
 
 - improve test coverage (-fhpc cabal flag)
 
-- how much of the support code - e.g. Swish.Utils.LookupMap - can now
-  be replaced with packages from hackage?
-
 - can we improve the processing of the commands, so that they
   exit on error immediately, with a single error message.
 

src/Data/LookupMap.hs

     , reverseLookupMap
     , keyOrder
     , mapFind, mapFindMaybe, mapContains
-    , mapReplace, mapReplaceOrAdd, mapReplaceAll, mapReplaceMap
+    , mapReplace, mapReplaceAll, mapReplaceMap
     , mapAdd, mapAddIfNew
     , mapDelete, mapDeleteAll
     , mapApplyToAll, mapTranslate
     , mapEq, mapKeys, mapVals
-    , mapSelect, mapMerge
+    , mapMerge
     , mapTranslateKeys, mapTranslateVals
     , mapTranslateEntries, mapTranslateEntriesM
 
     )
     where
 
+import Control.Arrow (first, second)
+
+import Data.Maybe (fromMaybe)
+import Data.Function (on)
+import Data.Ord (comparing)
+
+import Swish.Utils.ListHelpers (equiv)
+
 import qualified Data.Foldable as F
 import qualified Data.Traversable as T
 import qualified Data.List as L
 
-import Control.Arrow (first, second)
-
-import Data.Ord (comparing)
-
-import Swish.Utils.ListHelpers (equiv)
-
 #if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 701)
 import Data.Tuple (swap)
 #else
 --  is not defined here, for good reasons (which I forget right now, but
 --  something to do with the method dictionary being superfluous on
 --  an algebraic data type).
---
+
 -- |Define a lookup map based on a list of values.
 --
 data LookupMap a = LookupMap [a]
   deriving (Functor, F.Foldable, T.Traversable)
 
-{- 
-TODO: could add
+{-
 
-instance Monoid (LookupMap a) where
+To allow this Monoid instance, we would need UndecidableInstances.
+Also, mapMerge can error out which is not what we would want.
+
+instance (LookupEntryClass a k v, Eq a, Show a, Ord k) => Monoid (LookupMap a) where
     mempty = LookupMap []
     mappend = mapMerge
 
-but may need constraints on a, do not
-want to add instances at this time, and is
-it really useful? 
+We could use the following (perhaps with a L.nub on the result before sticling back
+into LookupMap) but it is unclear what the semantics are for repeated keys; it is
+likely to be left-biased but would leave duplicate keys in the list which could
+cause confusion at a later time (e.g. key removal). Many of the routines assume a single
+key (or single key,value) pair.
 
+instance (Eq a) => Monoid (LookupMap a) where
+    mempty = LookupMap []
+    (LookupMap a) `mappend` (LookupMap b) =
+        LookupMap (a `mappend` b))
 -}
 
 gLM :: LookupMap a -> [a]
 gLM (LookupMap es) = es
 
--- TODO:  See also 'mapEq'
---  (why not just use that for the Eq instance?  I don't know:  it's probably historic.)
---
-
 -- |Define equality of 'LookupMap' values based on equality of entries.
 --
 --  (This is possibly a poor definition, as it is dependent on ordering
 --  only for testing.)
 --
 instance (Eq a) => Eq (LookupMap a) where
-    LookupMap es1 == LookupMap es2 = es1 == es2
+    (==) = (==) `on` gLM
 
 -- |Define Show instance for LookupMap based on Showing the
 -- list of entries.
 instance (Show a ) => Show (LookupMap a) where
     show (LookupMap es) = "LookupMap " ++ show es
 
-{-
-TODO: should the LookupEntryClass constraint be removed from
-emptyLookupMap and makeLookupMap?
-
-I guess not since LookupMap is exported, so users can use
-that if they do not need the constraint.
--}
-
 -- |Empty lookup map of arbitrary (i.e. polymorphic) type.
 --
 emptyLookupMap :: (LookupEntryClass a k v) => LookupMap a
 
 -- |Function to create a `LookupMap` from a list of entries.
 --
---  Currently, this is trivial but future versions could be
---  more substantial.
---
-makeLookupMap :: (LookupEntryClass a k v) => [a] -> LookupMap a
+makeLookupMap :: 
+    (LookupEntryClass a k v) 
+    => [a]  -- ^ This list is not checked for duplicate entries, or
+            -- entries with the same key but different values.
+    -> LookupMap a
 makeLookupMap = LookupMap
 
--- |Return list of lookup map entries.
---
---  Currently, this is trivial but future versions could be
---  more substantial.
+-- |Returns a list of lookup map entries.
 --
 listLookupMap :: (LookupEntryClass a k v) => LookupMap a -> [a]
 listLookupMap = gLM
     =>  a -> a -> Ordering
 keyOrder = comparing entryKey
 
---  Local helper function to build a new LookupMap from
---  a new entry and an exiting map.
---
-mapCons :: a -> LookupMap a -> LookupMap a
-mapCons e (LookupMap es) = LookupMap (e:es)
-
 -- |Find key in lookup map and return corresponding value,
 --  otherwise return default supplied.
 --
-mapFind :: (LookupEntryClass a k v) => v -> k -> LookupMap a -> v
-mapFind def key (LookupMap es) = foldr match def es where
-    match ent alt
-        | key == entryKey ent   = entryVal ent
-        | otherwise             = alt
+mapFind :: 
+    (LookupEntryClass a k v) 
+    => v    -- ^ The default value.
+    -> k 
+    -> LookupMap a -> v
+mapFind def key = fromMaybe def . mapFindMaybe key
 
 -- |Find key in lookup map and return Just the corresponding value,
 --  otherwise return Nothing.
 mapContains (LookupMap es) key  = any match es where
     match ent = key == entryKey ent
 
--- |Replace an existing occurrence of a key a with a new key-value pair.
---    
---  The resulting lookup map has the same form as the original in all
---  other respects.  Assumes exactly one occurrence of the supplied key.
+-- |Replace the first occurrence of a key a with a new key-value pair,
+--  or add a new key-value pair if the supplied key is not already present.
 --
 mapReplace :: (LookupEntryClass a k v) =>
     LookupMap a -> a -> LookupMap a
+mapReplace (LookupMap []) newe      = LookupMap [newe]
 mapReplace (LookupMap (e:es)) newe
-    | entryKey e == entryKey newe       = LookupMap (newe:es)
-    | otherwise                         = mapAdd more e where
+    | entryKey e == entryKey newe   = LookupMap (newe:es)
+    | otherwise                     = mapAdd more e where
         more = mapReplace (LookupMap es) newe
-mapReplace _ newe =
-    error ("mapReplace: Key value not found in lookup table: "++
-           Prelude.show (entryKey newe))
 
--- |Replace an existing occurrence of a key a with a new key-value pair,
---  or add a new key-value pair if the supplied key is not already present.
---
-mapReplaceOrAdd :: (LookupEntryClass a k v) =>
-    a -> LookupMap a -> LookupMap a
-mapReplaceOrAdd newe (LookupMap (e:es))
-    | entryKey e == entryKey newe       = LookupMap (newe:es)
-    | otherwise                         = mapCons e more where
-        more = mapReplaceOrAdd newe (LookupMap es)
-mapReplaceOrAdd newe (LookupMap [])     = LookupMap [newe]
-
--- |Replace any occurrence of a key a with a new key-value pair.
+-- |Replace all occurrence of a key a with a new key-value pair.
 --
 --  The resulting lookup map has the same form as the original in all
 --  other respects.
 --
 mapReplaceAll :: (LookupEntryClass a k v) =>
     LookupMap a -> a -> LookupMap a
-mapReplaceAll (LookupMap (e:es)) newe   = mapCons e' more where
+mapReplaceAll l@(LookupMap []) _        = l
+mapReplaceAll (LookupMap (e:es)) newe   = mapAdd more e' where
     more = mapReplaceAll (LookupMap es) newe
     e'   = if entryKey e == entryKey newe then newe else e
-mapReplaceAll (LookupMap []) _          = LookupMap []
 
 -- |Replace any occurrence of a key in the first argument with a
 --  corresponding key-value pair from the second argument, if present.
 --
 mapReplaceMap :: (LookupEntryClass a k v) =>
     LookupMap a -> LookupMap a -> LookupMap a
-mapReplaceMap (LookupMap (e:es)) newmap = mapCons e' more where
+mapReplaceMap l@(LookupMap []) _ = l
+mapReplaceMap (LookupMap (e:es)) newmap = mapAdd more e' where
     more  = mapReplaceMap (LookupMap es) newmap
     e'    = newEntry (k, mapFind v k newmap)
     (k,v) = keyVal e
-mapReplaceMap (LookupMap []) _ = LookupMap []
 
 -- |Add supplied key-value pair to the lookup map.
 --
---  This is effectively an optimized case of 'mapReplaceOrAdd' or 'mapAddIfNew',
+--  This is effectively an optimized case of 'mapReplace' or 'mapAddIfNew',
 --  where the caller guarantees to avoid duplicate key values.
 --
 mapAdd :: LookupMap a -> a -> LookupMap a
-mapAdd emap e = mapCons e emap
+mapAdd (LookupMap es) e = LookupMap (e:es)
 
 -- |Add supplied key-value pair to the lookup map,
 --  only if the key value is not already present.
     LookupMap a -> a -> LookupMap a
 mapAddIfNew emap e = if mapContains emap (entryKey e)
                         then emap
-                        else mapCons e emap
+                        else mapAdd emap e
 
--- |Delete supplied key value from the lookup map.
+-- |Delete the first occurrence of the key from the lookup map.
 --
---  This function assumes exactly one occurrence.
+--  If the key does not exist in the map then no change is made.
 --
 mapDelete :: (LookupEntryClass a k v) =>
     LookupMap a -> k -> LookupMap a
+mapDelete l@(LookupMap []) _ = l
 mapDelete (LookupMap (e:es)) k
     | k == entryKey e   = LookupMap es
-    | otherwise         = mapCons e more where
+    | otherwise         = mapAdd more e where
         more = mapDelete (LookupMap es) k
-mapDelete _ k =
-    error ("mapDelete: Key value not found in lookup table: " ++ Prelude.show k)
 
--- |Delete any occurrence of a supplied key value from the lookup map.
+-- |Delete all occurrences of the key from the lookup map.
 --
 mapDeleteAll :: (LookupEntryClass a k v) =>
     LookupMap a -> k -> LookupMap a
+mapDeleteAll l@(LookupMap []) _ = l
 mapDeleteAll (LookupMap (e:es)) k =
-    if entryKey e == k then more else mapCons e more where
-        more = mapDeleteAll (LookupMap es) k
-mapDeleteAll (LookupMap []) _ = LookupMap []
-
+    let more = mapDeleteAll (LookupMap es) k
+    in if entryKey e == k then more else mapAdd more e
+        
 -- |Return a list of values obtained by applying a function to each key
 --  in the map.  Creates an alternative set of values that can be
---  retrieved using mapTranslate.
+--  retrieved using 'mapTranslate'.
 --
 mapApplyToAll :: (LookupEntryClass a k v) =>
     LookupMap a -> (k -> w) -> [w]
 --  may be obtained by 'mapApplyToAll'.
 --
 mapTranslate :: (LookupEntryClass a k v) =>
-    LookupMap a -> [w] -> k -> w -> w
+    LookupMap a  -- ^ Used to provide a list of keys.
+    -> [w]       -- ^ Value to use when key is found.
+    -> k         -- ^ Key to search for
+    -> w         -- ^ The default value if the key does not exist.
+    -> w
 mapTranslate (LookupMap (e:es)) (w:ws) k def
     | k == entryKey e   = w
     | otherwise         = mapTranslate (LookupMap es) ws k def
 -- |Compare two lookup maps for equality.
 --
 --  Two maps are equal if they have the same set of keys, and if
---  each key maps to an equivalent value.
+--  each key maps to an equivalent value. This is only guaranteed
+--  if the maps do not contain duplicate entries.
 --
 mapEq :: (LookupEntryClass a k v, Eq v) =>
     LookupMap a -> LookupMap a -> Bool
         ks1 = mapKeys es1
         ks2 = mapKeys es2
 
--- |Return the list of keys in a supplied LookupMap
+-- |Return the list of distinct keys in a supplied LookupMap
 --
 mapKeys :: (LookupEntryClass a k v) =>
     LookupMap a -> [k]
-mapKeys (LookupMap es) = L.nub $ map entryKey es
+mapKeys = L.nub . gLM . fmap entryKey
 
 -- |Return list of distinct values in a supplied LookupMap
 --
 mapVals :: (Eq v, LookupEntryClass a k v) =>
     LookupMap a -> [v]
-mapVals (LookupMap es) = L.nub $ map entryVal es
-
--- |Select portion of a lookup map that corresponds to
---  a supplied list of keys
---
-mapSelect :: (LookupEntryClass a k v) =>
-    LookupMap a -> [k] -> LookupMap a
-mapSelect (LookupMap es) ks =
-    LookupMap $ filter (keyIn ks) es
-    where
-        keyIn iks e = entryKey e `elem` iks
+mapVals = L.nub . gLM . fmap entryVal
 
 -- |Merge two lookup maps, ensuring that if the same key appears
 --  in both maps it is associated with the same value.
 -- |A monadic form of `mapTranslateEntries` which is
 -- the same as `Data.Traversable.mapM`.
 --
--- Since `LookupMap` now has a `Data.Traversable.Traversable` instance
--- this is just `T.mapM`.
---
 mapTranslateEntriesM :: (Monad m)
     => (a1 -> m a2) -> LookupMap a1 -> m (LookupMap a2)
 mapTranslateEntriesM = T.mapM 

src/Swish/RDF/Graph.hs

 import Data.Hashable (hashWithSalt)
 import Data.List (intersect, union, foldl')
 import Data.LookupMap (LookupMap(..), LookupEntryClass(..))
-import Data.LookupMap (listLookupMap, mapFind, mapFindMaybe, mapReplaceOrAdd, mapAddIfNew, mapVals, mapKeys )
+import Data.LookupMap (listLookupMap, mapFind, mapFindMaybe, mapReplace, mapAddIfNew, mapVals, mapKeys )
 import Data.Ord (comparing)
 import Data.Word (Word32)
 
 
 -- | Add (or replace) a formula.
 setFormula     :: (Label lb) => Formula lb -> NSGraph lb -> NSGraph lb
-setFormula f g = g { formulae=mapReplaceOrAdd f (formulae g) }
+setFormula f g = g { formulae=mapReplace (formulae g) f }
 
 {-|
 Add an arc to the graph. It does not relabel any blank nodes in the input arc,

src/Swish/RDF/Parser/N3.hs

 
 import Data.Char (isSpace, isDigit, ord, isAsciiLower) 
 import Data.LookupMap (LookupMap(..), LookupEntryClass(..))
-import Data.LookupMap (mapFind, mapFindMaybe, mapReplaceOrAdd, mapAdd, mapReplace)
+import Data.LookupMap (mapFind, mapFindMaybe, mapAdd, mapReplace)
 import Data.Maybe (fromMaybe, fromJust)
 import Data.Word (Word32)
 
 setPrefix :: Maybe T.Text -> URI -> N3State -> N3State
 setPrefix pre uri st =  st { prefixUris=p' }
     where
-        p' = mapReplaceOrAdd (makeNamespace pre uri) (prefixUris st)
+        p' = mapReplace (prefixUris st) (makeNamespace pre uri) 
 
 -- | Set name for special syntax element
 setSName :: String -> ScopedName -> N3State -> N3State
 setSName nam snam st =  st { syntaxUris=s' }
     where
-        s' = mapReplaceOrAdd (nam,snam) (syntaxUris st)
+        s' = mapReplace (syntaxUris st) (nam,snam)
 
 setSUri :: String -> URI -> N3State -> N3State
 setSUri nam = setSName nam . makeURIScopedName
   - could we use the reverse lookupmap functionality to
     find if the given namespace URI is in the namespace
     list? If it is, use it's key otherwise do a
-    mapReplaceOrAdd for the input namespace.
+    mapReplace for the input namespace.
     
 -}
 operatorLabel :: ScopedName -> N3Parser RDFLabel
   let stmt = arc s p o
       oldp = prefixUris ost
       ogs = graphState ost
-      newp = mapReplaceOrAdd (getScopeNamespace dtype) oldp
+      newp = mapReplace oldp (getScopeNamespace dtype)
   stUpdate $ \st -> st { prefixUris = newp, graphState = addArc stmt ogs }
 addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))
 

src/Swish/RDF/Parser/Turtle.hs

 
 import Data.Char (ord, isAsciiLower, isAsciiUpper, isDigit) 
 import Data.LookupMap (LookupMap(..), LookupEntryClass(..))
-import Data.LookupMap (mapFindMaybe, mapReplaceOrAdd, mapAdd, mapReplace)
+import Data.LookupMap (mapFindMaybe, mapAdd, mapReplace)
 import Data.Maybe (fromMaybe, fromJust)
 import Data.Word (Word32)
 
 setPrefix :: Maybe T.Text -> URI -> TurtleState -> TurtleState
 setPrefix pre uri st =  st { prefixUris=p' }
     where
-        p' = mapReplaceOrAdd (makeNamespace pre uri) (prefixUris st)
+        p' = mapReplace (prefixUris st) (makeNamespace pre uri) 
 
 -- | Change the base
 setBase :: URI -> TurtleState -> TurtleState
   let stmt = arc s p o
       oldp = prefixUris ost
       ogs = graphState ost
-      newp = mapReplaceOrAdd (getScopeNamespace dtype) oldp
+      newp = mapReplace oldp (getScopeNamespace dtype)
   stUpdate $ \st -> st { prefixUris = newp, graphState = addArc stmt ogs }
 addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))
 
   - could we use the reverse lookupmap functionality to
     find if the given namespace URI is in the namespace
     list? If it is, use it's key otherwise do a
-    mapReplaceOrAdd for the input namespace.
+    mapReplace for the input namespace.
     
 -}
 operatorLabel :: ScopedName -> TurtleParser RDFLabel

src/Swish/Script.hs

 import Control.Monad (unless, when, liftM, void)
 import Control.Monad.State (modify, gets, lift)
 
-import Data.LookupMap (mapReplaceOrAdd)
+import Data.LookupMap (mapReplace)
 import Data.Monoid (Monoid(..))
 
 import Network.URI (URI(..))
             ; let egs = sequence esg    -- Either String [RDFGraph]
             ; let fgs = case egs of
                     Left  er -> setError  (errmsg++er)
-                    Right gs -> modGraphs (mapReplaceOrAdd (NamedGraph nam gs))
+                    Right gs -> modGraphs (flip mapReplace (NamedGraph nam gs))
             ; modify fgs
             }
 
             ; let fgs = case egs of
                     Left  er -> setError  (errmsg++er)
                     Right [] -> setError  (errmsg++"No graphs to merge")
-                    Right gs -> modGraphs (mapReplaceOrAdd (NamedGraph nam [g]))
+                    Right gs -> modGraphs (flip mapReplace (NamedGraph nam [g]))
                             where g = foldl1 merge gs
             ; modify fgs
             }
                             newRule = makeRDFClosureRule rn agrs cgr
                         in
                         case composeSequence vbms of
-                            Just vm -> modRules (mapReplaceOrAdd (newRule vm))
+                            Just vm -> modRules (flip mapReplace (newRule vm))
                             Nothing -> setError errmsg4
             ; modify frl
             }
                     (Left er,_) -> setError (errmsg1++er)
                     (_,Left er) -> setError (errmsg2++er)
                     (Right ags,Right rls) ->
-                        modRulesets (mapReplaceOrAdd rs)
+                        modRulesets (flip mapReplace rs)
                         where
                             rs = makeRuleset (getScopeNamespace sn) ags rls
             ; modify frs
                     (Left er,_) -> setError (errmsg1++er)
                     (_,Left er) -> setError (errmsg2++er)
                     (Right cgr,Right dts) ->
-                        modRulesets (mapReplaceOrAdd rs)
+                        modRulesets (flip mapReplace rs)
                         where
                             rs  = makeRuleset (getScopeNamespace sn) [] rls
                             rls = concatMap (`typeMkRules` cgr) dts
                     (Left er,_) -> setError (errmsg1++er)
                     (_,Left er) -> setError (errmsg2++er)
                     (Right rl,Right ags) ->
-                        modGraphs (mapReplaceOrAdd (NamedGraph cn [cg]))
+                        modGraphs (flip mapReplace (NamedGraph cn [cg]))
                         where
                             cg = case fwdApply rl ags of
                                 []  -> mempty
                     (Left er,_) -> setError (errmsg1++er)
                     (_,Left er) -> setError (errmsg2++er)
                     (Right rl,Right cg) ->
-                        modGraphs (mapReplaceOrAdd (NamedGraph an ags))
+                        modGraphs (flip mapReplace (NamedGraph an ags))
                         where
                             ags  = map mergegr (bwdApply rl cg)
                             mergegr grs = case grs of
   * Use @Word32@ rather than @Int@ for label indexes (@Swish.GraphMatch.LabelIndex@)
   and in the bnode counts when formatting to N3/Turtle.
   .
+  * Minor clean up of the @LookupMap@ module: @mergeReplaceOrAdd@ and @mergeReplace@
+  are now combined into @mergeReplace@; @mapSelect@ has been removed; documentation
+  slightly improved, and a few minor internal clean ups.
+  .
   * Clarified that @Swish.RDF.RDFDatatypeXsdDecimal@ is for @xsd:decimal@ rather
   than @xsd:double@.
   .

tests/LookupMapTest.hs

     , makeLookupMap
     , reverseLookupMap
     , mapFind, mapContains
-    , mapReplace, mapReplaceOrAdd, mapReplaceAll, mapReplaceMap
+    , mapReplace, mapReplaceAll, mapReplaceMap
     , mapAdd, mapAddIfNew
     , mapDelete, mapDeleteAll
     , mapApplyToAll, mapTranslate
     , mapEq, mapKeys, mapVals
-    , mapSelect, mapMerge
+    , mapMerge
     , mapTranslateKeys, mapTranslateVals
     , mapTranslateEntries, mapTranslateEntriesM
     )
 lm22 = mapReplaceMap lm05 $ newMap [(9,"zzz22"),(1,"aaa22")]
 lm33 = mapAddIfNew lm22 $ newEntry (1,"aaa33")
 lm34 = mapAddIfNew lm22 $ newEntry (4,"ddd34")
-lm35 = mapReplaceOrAdd (newEntry (1,"aaa35")) lm22
-lm36 = mapReplaceOrAdd (newEntry (4,"ddd36")) lm22
+lm35 = mapReplace lm22 (newEntry (1,"aaa35"))
+lm36 = mapReplace lm22 (newEntry (4,"ddd36"))
 
 testLookupMapSuite :: Test
 testLookupMapSuite = 
 
 lm101, lm102, lm103, lm104 :: TestMap
 lm101 = mapAdd lm03 $ newEntry (4,"ddd")
+{-
 lm102 = mapSelect lm101 [1,3]
 lm103 = mapSelect lm101 [2,4]
 lm104 = mapSelect lm101 [2,3]
+-}
 
-mapSelectSuite :: Test
-mapSelectSuite = 
-  TestList
-  [ testLookupMap "101" lm101 [(4,"ddd"),(3,"ccc"),(2,"bbb"),(1,"aaa")]
-  , testLookupMap "102" lm102 [(3,"ccc"),(1,"aaa")]
-  , testLookupMap "103" lm103 [(4,"ddd"),(2,"bbb")]
-  , testLookupMap "104" lm104 [(3,"ccc"),(2,"bbb")]
-  ]
-  
+lm102 = mapAdd (mapAdd (newMap []) (newEntry (1,"aaa"))) $ newEntry (3,"ccc")
+lm103 = mapAdd (mapAdd (newMap []) (newEntry (2,"bbb"))) $ newEntry (4,"ddd")
+lm104 = mapAdd (mapAdd (newMap []) (newEntry (2,"bbb"))) $ newEntry (3,"ccc")
+
 lm105, lm106, lm107, lm108 :: TestMap
 lm105 = mapMerge lm102 lm103
 lm106 = mapMerge lm102 lm104
 mapMergeSuite :: Test
 mapMergeSuite =
   TestList
-  [ testLookupMap "105" lm105 [(1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")]
+  [ testLookupMap "101" lm101 [(4,"ddd"),(3,"ccc"),(2,"bbb"),(1,"aaa")]
+  , testLookupMap "105" lm105 [(1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")]
   , testLookupMap "106" lm106 [(1,"aaa"),(2,"bbb"),(3,"ccc")]
   , testLookupMap "107" lm107 [(2,"bbb"),(3,"ccc"),(4,"ddd")]
   , testLookupMap "108" lm108 [(1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")]
   , testMapKeysSuite
   , testMapValsSuite
   , testMapEqSuite
-  , mapSelectSuite
   , mapMergeSuite
   , mapTranslateSuite
   ]