extra 0.3.2 → 0.4
raw patch · 10 files changed
+222/−66 lines, 10 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Data.List.Extra: dropAround :: (a -> Bool) -> [a] -> [a]
- Data.List.Extra: groupSortOn :: Ord a => (k -> a) -> [(k, v)] -> [(k, [v])]
- Data.Tuple.Extra: both3 :: (a -> b) -> (a, a, a) -> (b, b, b)
- Data.Tuple.Extra: dupe3 :: a -> (a, a, a)
- Data.Tuple.Extra: first3 :: (a -> a') -> (a, b, c) -> (a', b, c)
- Data.Tuple.Extra: second3 :: (b -> b') -> (a, b, c) -> (a, b', c)
- Data.Tuple.Extra: third3 :: (c -> c') -> (a, b, c) -> (a, b, c')
- Extra: both3 :: (a -> b) -> (a, a, a) -> (b, b, b)
- Extra: dropAround :: (a -> Bool) -> [a] -> [a]
- Extra: dupe3 :: a -> (a, a, a)
- Extra: first3 :: (a -> a') -> (a, b, c) -> (a', b, c)
- Extra: groupSortOn :: Ord a => (k -> a) -> [(k, v)] -> [(k, [v])]
- Extra: second3 :: (b -> b') -> (a, b, c) -> (a, b', c)
- Extra: third3 :: (c -> c') -> (a, b, c) -> (a, b, c')
Files
- CHANGES.txt +4/−0
- Generate.hs +2/−2
- extra.cabal +1/−1
- src/Control/Concurrent/Extra.hs +1/−1
- src/Data/List/Extra.hs +122/−32
- src/Data/Tuple/Extra.hs +23/−20
- src/Extra.hs +2/−2
- src/Numeric/Extra.hs +5/−5
- src/System/Process/Extra.hs +5/−0
- test/TestGen.hs +57/−3
CHANGES.txt view
@@ -1,5 +1,9 @@ Changelog for Extra +0.4+ Remove all but the extractors on triples+ Remove groupSortOn+ Remove dropAround 0.3.2 Remove use of ===, allows older QuickCheck versions 0.3.1
Generate.hs view
@@ -61,8 +61,8 @@ tweakTest x | Just x <- stripSuffix " == undefined" x = if not $ "\\" `isPrefixOf` x then- "erroneous $ " ++ x+ "erroneous $ " ++ trim x else- let (a,b) = breakOn "->" x+ let (a,b) = breakOn "->" $ trim x in a ++ "-> erroneous $ " ++ drop 2 b | otherwise = x
extra.cabal view
@@ -1,7 +1,7 @@ cabal-version: >= 1.10 build-type: Simple name: extra-version: 0.3.2+version: 0.4 license: BSD3 license-file: LICENSE category: Development
src/Control/Concurrent/Extra.hs view
@@ -86,7 +86,7 @@ newtype Lock = Lock (MVar ()) --- | Create a 'newLock'.+-- | Create a new 'Lock'. newLock :: IO Lock newLock = fmap Lock $ newMVar ()
src/Data/List/Extra.hs view
@@ -6,9 +6,9 @@ -- Some of the names and semantics were inspired by the @text@ package. module Data.List.Extra( module Data.List,- lower, upper, trim, trimStart, trimEnd, dropAround, word1, drop1,+ lower, upper, trim, trimStart, trimEnd, word1, drop1, list, uncons, unsnoc, cons, snoc,- groupSort, groupSortOn, nubOn, groupOn, sortOn,+ groupSort, nubOn, groupOn, sortOn, repeatedly, for, disjoint, allSame, anySame, dropEnd, takeEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix,@@ -18,8 +18,8 @@ ) where import Data.List+import Data.Maybe import Data.Function-import Data.Ord import Data.Char import Data.Tuple.Extra @@ -117,67 +117,122 @@ snoc :: [a] -> a -> [a] snoc xs x = xs ++ [x] ++-- | Take a number of elements from the end of the list.+--+-- > takeEnd 3 "hello" == "llo"+-- > takeEnd 5 "bye" == "bye"+-- > takeEnd (-1) "bye" == ""+-- > \i xs -> takeEnd i xs `isSuffixOf` xs+-- > \i xs -> length (takeEnd i xs) == min (max 0 i) (length xs) takeEnd :: Int -> [a] -> [a]-takeEnd i = reverse . take i . reverse+takeEnd i xs = f xs (drop i xs)+ where f (x:xs) (y:ys) = f xs ys+ f xs _ = xs +-- | Drop a number of elements from the end of the list.+--+-- > dropEnd 3 "hello" == "he"+-- > dropEnd 5 "bye" == ""+-- > dropEnd (-1) "bye" == "bye"+-- > \i xs -> dropEnd i xs `isPrefixOf` xs+-- > \i xs -> length (dropEnd i xs) == max 0 (length xs - max 0 i)+-- > \i -> take 3 (dropEnd 5 [i..]) == take 3 [i..] dropEnd :: Int -> [a] -> [a]-dropEnd i = reverse . drop i . reverse+dropEnd i xs = f xs (drop i xs)+ where f (x:xs) (y:ys) = x : f xs ys+ f _ _ = [] ++-- | A merging of 'unzip' and 'concat'.+--+-- > concatUnzip [("a","AB"),("bc","C")] == ("abc","ABC") concatUnzip :: [([a], [b])] -> ([a], [b]) concatUnzip = (concat *** concat) . unzip +-- | A merging of 'unzip3' and 'concat'.+--+-- > concatUnzip3 [("a","AB",""),("bc","C","123")] == ("abc","ABC","123") concatUnzip3 :: [([a],[b],[c])] -> ([a],[b],[c]) concatUnzip3 xs = (concat a, concat b, concat c) where (a,b,c) = unzip3 xs +-- | A version of 'takeWhile' operating from the end.+--+-- > takeWhileEnd even [2,3,4,6] == [4,6] takeWhileEnd :: (a -> Bool) -> [a] -> [a] takeWhileEnd f = reverse . takeWhile f . reverse -trim, trimStart, trimEnd :: String -> String+-- | Remove spaces from the start of a string, see 'trim'.+trimStart :: String -> String trimStart = dropWhile isSpace++-- | Remove spaces from the end of a string, see 'trim'.+trimEnd :: String -> String trimEnd = dropWhileEnd isSpace-trim = dropAround isSpace --- | Documentation about lowercase+-- | Remove spaces from either side of a string. A combination of 'trimEnd' and 'trimStart'. --+-- > trim " hello " == "hello"+-- > trimStart " hello " == "hello "+-- > trimEnd " hello " == " hello"+-- > \s -> trim s == trimEnd (trimStart s)+trim :: String -> String+trim = trimEnd . trimStart++-- | Convert a string to lower case.+-- -- > lower "This is A TEST" == "this is a test" -- > lower "" == "" lower :: String -> String lower = map toLower +-- | Convert a string to upper case.+--+-- > upper "This is A TEST" == "THIS IS A TEST"+-- > upper "" == "" upper :: String -> String upper = map toUpper -dropAround :: (a -> Bool) -> [a] -> [a]-dropAround f = dropWhileEnd f . dropWhile f - word1 :: String -> (String, String) word1 x = second (dropWhile isSpace) $ break isSpace $ dropWhile isSpace x +-- | A version of 'sort' where the comparison is done on some extracted value.+--+-- > sortOn fst [(3,"z"),(1,""),(3,"a")] == [(1,""),(3,"z"),(3,"a")] sortOn :: Ord b => (a -> b) -> [a] -> [a]-sortOn f = sortBy (comparing f)+sortOn f = sortBy (compare `on` f) +-- | A version of 'group' where the equality is done on some extracted value. groupOn :: Eq b => (a -> b) -> [a] -> [[a]] groupOn f = groupBy ((==) `on` f) +-- | A version of 'nub' where the equality is done on some extracted value. nubOn :: Eq b => (a -> b) -> [a] -> [a] nubOn f = nubBy ((==) `on` f) +-- | A combination of 'group' and 'sort'.+--+-- > groupSort [(1,'t'),(3,'t'),(2,'e'),(2,'s')] == [(1,"t"),(2,"es"),(3,"t")]+-- > \xs -> map fst (groupSort xs) == sort (nub (map fst xs))+-- > \xs -> concatMap snd (groupSort xs) == map snd (sortOn fst xs) groupSort :: Ord k => [(k, v)] -> [(k, [v])]-groupSort = groupSortOn id--groupSortOn :: Ord a => (k -> a) -> [(k, v)] -> [(k, [v])]-groupSortOn f = map (\x -> (fst $ head x, map snd x)) . groupOn (f . fst) . sortOn (f . fst)+groupSort = map (\x -> (fst $ head x, map snd x)) . groupOn fst . sortOn fst +-- | Merge two lists which are assumed to be ordered.+--+-- > merge "ace" "bd" == "abcde"+-- > \xs ys -> merge (sort xs) (sort ys) == sort (xs ++ ys) merge :: Ord a => [a] -> [a] -> [a] merge = mergeBy compare +-- | Like 'merge', but with a custom ordering function. mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a] mergeBy f xs [] = xs mergeBy f [] ys = ys@@ -185,7 +240,16 @@ | f x y /= GT = x : mergeBy f xs (y:ys) | otherwise = y : mergeBy f (x:xs) ys ++-- | Replace a subsequence everywhere it occurs. The first argument must+-- not be the empty list.+--+-- > replace "el" "_" "Hello Bella" == "H_lo B_la"+-- > replace "el" "e" "Hello" == "Helo"+-- > replace "" "e" "Hello" == undefined+-- > \xs ys -> not (null xs) ==> replace xs xs ys == ys replace :: Eq a => [a] -> [a] -> [a] -> [a]+replace [] _ _ = error "Extra.replace, first argument cannot be empty" replace from to xs | Just xs <- stripPrefix from xs = to ++ replace from to xs replace from to (x:xs) = x : replace from to xs replace from to [] = []@@ -208,12 +272,23 @@ spanEnd f = breakEnd (not . f) +-- | A variant of 'words' with a custom test. In particular,+-- adjacent separators are discarded, as are leading or trailing separators.+--+-- > wordsBy (== ':') "::xyz:abc::123::" == ["xyz","abc","123"]+-- > \s -> wordsBy isSpace s == words s wordsBy :: (a -> Bool) -> [a] -> [[a]] wordsBy f s = case dropWhile f s of [] -> [] x:xs -> (x:w) : wordsBy f (drop1 z) where (w,z) = break f xs +-- | A variant of 'lines' with a custom test. In particular,+-- if there is a trailing separator it will be discarded.+--+-- > linesBy (== ':') "::xyz:abc::123::" == ["","","xyz","abc","","123",""]+-- > \s -> linesBy (== '\n') s == lines s+-- > linesBy (== ';') "my;list;here;" == ["my","list","here"] linesBy :: (a -> Bool) -> [a] -> [[a]] linesBy f [] = [] linesBy f s = cons $ case break f s of@@ -223,10 +298,22 @@ where cons ~(h, t) = h : t -- to fix a space leak, see the GHC defn of lines +-- | Find the first element of a list for which the operation returns 'Just', along+-- with the result of the operation. Like 'find' but useful where the function also+-- computes some expensive information that can be reused. Particular useful+-- when the function is monadic, see 'firstJustM'.+--+-- > firstJust id [Nothing,Just 3] == Just 3+-- > firstJust id [Nothing,Nothing] == Nothing firstJust :: (a -> Maybe b) -> [a] -> Maybe b-firstJust p [] = Nothing-firstJust p (x:xs) = maybe (firstJust p xs) Just (p x)+firstJust f = listToMaybe . mapMaybe f ++-- | Equivalent to @drop 1@, but likely to be faster and a single lexeme.+--+-- > drop1 "" == ""+-- > drop1 "test" == "est"+-- > \xs -> drop 1 xs == drop1 xs drop1 :: [a] -> [a] drop1 [] = [] drop1 (x:xs) = xs@@ -237,13 +324,8 @@ -- is the prefix of @haystack@ before @needle@ is matched. The second -- is the remainder of @haystack@, starting with the match. ----- Examples:--- -- > breakOn "::" "a::b::c" == ("a", "::b::c") -- > breakOn "/" "foobar" == ("foobar", "")------ Laws:--- -- > \needle haystack -> let (prefix,match) = breakOn needle haystack in prefix ++ match == haystack breakOn :: Eq a => [a] -> [a] -> ([a], [a]) breakOn needle haystack | needle `isPrefixOf` haystack = ([], haystack)@@ -266,15 +348,10 @@ -- list argument, consuming the delimiter. An empty delimiter is -- invalid, and will cause an error to be raised. ----- Examples:--- -- > splitOn "\r\n" "a\r\nb\r\nd\r\ne" == ["a","b","d","e"] -- > splitOn "aaa" "aaaXaaaXaaaXaaa" == ["","X","X","X",""] -- > splitOn "x" "x" == ["",""] -- > splitOn "x" "" == [""]------ and--- -- > \s x -> s /= "" ==> intercalate s (splitOn s x) == x -- > \c x -> splitOn [c] x == split (==c) x splitOn :: Eq a => [a] -> [a] -> [[a]]@@ -287,10 +364,12 @@ -- | Splits a list into components delimited by separators, -- where the predicate returns True for a separator element. The -- resulting components do not contain the separators. Two adjacent--- separators result in an empty component in the output. eg.+-- separators result in an empty component in the output. ----- > split (=='a') "aabbaca" == ["","","bb","c",""]--- > split (=='a') "" == [""]+-- > split (== 'a') "aabbaca" == ["","","bb","c",""]+-- > split (== 'a') "" == [""]+-- > split (== ':') "::xyz:abc::123::" == ["","","xyz","abc","","123","",""]+-- > split (== ',') "my,list,here" == ["my","list","here"] split :: (a -> Bool) -> [a] -> [[a]] split f [] = [[]] split f (x:xs) | f x = [] : split f xs@@ -304,7 +383,18 @@ -- | A version of 'dropWhileEnd' but with different strictness properties.--- Often outperforms if the list is short or the test is expensive.+-- The function 'dropWhileEnd' can be used on an infinite list and tests the property+-- on each character. In contrast, 'dropWhileEnd'' is strict in the spine of the list+-- but only tests the trailing suffix.+-- This version usually outperforms 'dropWhileEnd' if the list is short or the test is expensive.+-- Note the tests below cover both the prime and non-prime variants.+--+-- > dropWhileEnd isSpace "ab cde " == "ab cde"+-- > dropWhileEnd' isSpace "ab cde " == "ab cde"+-- > last (dropWhileEnd even [undefined,3]) == undefined+-- > last (dropWhileEnd' even [undefined,3]) == 3+-- > head (dropWhileEnd even (3:undefined)) == 3+-- > head (dropWhileEnd' even (3:undefined)) == undefined dropWhileEnd' :: (a -> Bool) -> [a] -> [a] dropWhileEnd' p = foldr (\x xs -> if null xs && p x then [] else x : xs) []
src/Data/Tuple/Extra.hs view
@@ -8,8 +8,8 @@ first, second, (***), (&&&), -- * More pair operations dupe, both,- -- * Operations on triples- fst3, snd3, thd3, first3, second3, third3, dupe3, both3,+ -- * Extract from a triple+ fst3, snd3, thd3 ) where import Data.Tuple@@ -18,48 +18,51 @@ infixr 3 ***, &&& -- | Update the first component of a pair.+--+-- > first succ (1,"test") == (2,"test") first :: (a -> a') -> (a, b) -> (a', b) first = Arrow.first -- | Update the second component of a pair.+--+-- > second reverse (1,"test") == (1,"tset") second :: (b -> b') -> (a, b) -> (a, b') second = Arrow.second +-- | Given two functions, apply one to the first component and one to the second.+-- A specialised version of 'Control.Arrow.***'.+--+-- > (succ *** reverse) (1,"test") == (2,"tset") (***) :: (a -> a') -> (b -> b') -> (a, b) -> (a', b') (***) = (Arrow.***) +-- | Given two functions, apply both to a single argument to form a pair.+-- A specialised version of 'Control.Arrow.&&&'.+--+-- > (succ &&& pred) 1 == (2,0) (&&&) :: (a -> b) -> (a -> c) -> a -> (b, c) (&&&) = (Arrow.&&&) +-- | Duplicate a single value into a pair.+--+-- > dupe 12 == (12, 12) dupe :: a -> (a,a) dupe x = (x,x) +-- | Apply a single function to both componenets of a pair.+--+-- > both succ (1,2) == (2,3) both :: (a -> b) -> (a, a) -> (b, b) both f (x,y) = (f x, f y) +-- | Extract the 'fst' of a triple. fst3 :: (a,b,c) -> a fst3 (a,b,c) = a +-- | Extract the 'snd' of a triple. snd3 :: (a,b,c) -> b snd3 (a,b,c) = b +-- | Extract the final element of a triple. thd3 :: (a,b,c) -> c thd3 (a,b,c) = c---- | Update the first component of a triple.-first3 :: (a -> a') -> (a, b, c) -> (a', b, c)-first3 f (a,b,c) = (f a, b, c)---- | Update the second component of a triple.-second3 :: (b -> b') -> (a, b, c) -> (a, b', c)-second3 f (a,b,c) = (a, f b, c)---- | Update the third component of a triple.-third3 :: (c -> c') -> (a, b, c) -> (a, b, c')-third3 f (a,b,c) = (a, b, f c)--dupe3 :: a -> (a,a,a)-dupe3 x = (x,x,x)--both3 :: (a -> b) -> (a, a, a) -> (b, b, b)-both3 f (x,y,z) = (f x, f y, f z)
src/Extra.hs view
@@ -20,10 +20,10 @@ modifyIORef', writeIORef', atomicModifyIORef', atomicWriteIORef, atomicWriteIORef', -- * Data.List.Extra -- | Extra functions available in @"Data.List.Extra"@.- lower, upper, trim, trimStart, trimEnd, dropAround, word1, drop1, list, uncons, unsnoc, cons, snoc, groupSort, groupSortOn, nubOn, groupOn, sortOn, repeatedly, for, disjoint, allSame, anySame, dropEnd, takeEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, concatUnzip, concatUnzip3, merge, mergeBy, replace, wordsBy, linesBy, firstJust, breakOn, breakOnEnd, splitOn, split, chunksOf,+ lower, upper, trim, trimStart, trimEnd, word1, drop1, list, uncons, unsnoc, cons, snoc, groupSort, nubOn, groupOn, sortOn, repeatedly, for, disjoint, allSame, anySame, dropEnd, takeEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, concatUnzip, concatUnzip3, merge, mergeBy, replace, wordsBy, linesBy, firstJust, breakOn, breakOnEnd, splitOn, split, chunksOf, -- * Data.Tuple.Extra -- | Extra functions available in @"Data.Tuple.Extra"@.- first, second, (***), (&&&), dupe, both, fst3, snd3, thd3, first3, second3, third3, dupe3, both3,+ first, second, (***), (&&&), dupe, both, fst3, snd3, thd3, -- * Numeric.Extra -- | Extra functions available in @"Numeric.Extra"@. showDP, intToDouble, intToFloat, floatToDouble, doubleToFloat,
src/Numeric/Extra.hs view
@@ -11,7 +11,7 @@ --------------------------------------------------------------------- -- Data.String --- | Show a number to a number of decimal places.+-- | Show a number to a fixed number of decimal places. -- -- > showDP 4 pi == "3.1416" -- > showDP 0 pi == "3"@@ -24,19 +24,19 @@ --------------------------------------------------------------------- -- Numeric --- | Specialised numeric conversion.+-- | Specialised numeric conversion, type restricted version of 'fromIntegral'. intToDouble :: Int -> Double intToDouble = fromIntegral --- | Specialised numeric conversion.+-- | Specialised numeric conversion, type restricted version of 'fromIntegral'. intToFloat :: Int -> Float intToFloat = fromIntegral --- | Specialised numeric conversion.+-- | Specialised numeric conversion, type restricted version of 'realToFrac'. floatToDouble :: Float -> Double floatToDouble = realToFrac --- | Specialised numeric conversion.+-- | Specialised numeric conversion, type restricted version of 'realToFrac'. doubleToFloat :: Double -> Float doubleToFloat = realToFrac
src/System/Process/Extra.hs view
@@ -11,6 +11,8 @@ import System.Exit +-- | A version of 'system' that also captures the output, both 'stdout' and 'stderr'.+-- Returns a pair of the exit code and the output. systemOutput :: String -> IO (ExitCode, String) systemOutput x = withTempFile $ \file -> do exit <- withFile file WriteMode $ \h -> do@@ -19,12 +21,15 @@ fmap (exit,) $ readFile' file +-- | A version of 'system' that throws an error if the 'ExitCode' is not 'ExitSuccess'. system_ :: String -> IO () system_ x = do res <- system x when (res /= ExitSuccess) $ error $ "Failed when running system command: " ++ x +-- | A version of 'system' that captures the output (both 'stdout' and 'stderr')+-- and throws an error if the 'ExitCode' is not 'ExitSuccess'. systemOutput_ :: String -> IO String systemOutput_ x = do (res,out) <- systemOutput x
test/TestGen.hs view
@@ -72,14 +72,54 @@ testGen "\\x xs -> uncons (cons x xs) == Just (x,xs)" $ \x xs -> uncons (cons x xs) == Just (x,xs) testGen "snoc \"tes\" 't' == \"test\"" $ snoc "tes" 't' == "test" testGen "\\xs x -> unsnoc (snoc xs x) == Just (xs,x)" $ \xs x -> unsnoc (snoc xs x) == Just (xs,x)+ testGen "takeEnd 3 \"hello\" == \"llo\"" $ takeEnd 3 "hello" == "llo"+ testGen "takeEnd 5 \"bye\" == \"bye\"" $ takeEnd 5 "bye" == "bye"+ testGen "takeEnd (-1) \"bye\" == \"\"" $ takeEnd (-1) "bye" == ""+ testGen "\\i xs -> takeEnd i xs `isSuffixOf` xs" $ \i xs -> takeEnd i xs `isSuffixOf` xs+ testGen "\\i xs -> length (takeEnd i xs) == min (max 0 i) (length xs)" $ \i xs -> length (takeEnd i xs) == min (max 0 i) (length xs)+ testGen "dropEnd 3 \"hello\" == \"he\"" $ dropEnd 3 "hello" == "he"+ testGen "dropEnd 5 \"bye\" == \"\"" $ dropEnd 5 "bye" == ""+ testGen "dropEnd (-1) \"bye\" == \"bye\"" $ dropEnd (-1) "bye" == "bye"+ testGen "\\i xs -> dropEnd i xs `isPrefixOf` xs" $ \i xs -> dropEnd i xs `isPrefixOf` xs+ testGen "\\i xs -> length (dropEnd i xs) == max 0 (length xs - max 0 i)" $ \i xs -> length (dropEnd i xs) == max 0 (length xs - max 0 i)+ testGen "\\i -> take 3 (dropEnd 5 [i..]) == take 3 [i..]" $ \i -> take 3 (dropEnd 5 [i..]) == take 3 [i..]+ testGen "concatUnzip [(\"a\",\"AB\"),(\"bc\",\"C\")] == (\"abc\",\"ABC\")" $ concatUnzip [("a","AB"),("bc","C")] == ("abc","ABC")+ testGen "concatUnzip3 [(\"a\",\"AB\",\"\"),(\"bc\",\"C\",\"123\")] == (\"abc\",\"ABC\",\"123\")" $ concatUnzip3 [("a","AB",""),("bc","C","123")] == ("abc","ABC","123")+ testGen "takeWhileEnd even [2,3,4,6] == [4,6]" $ takeWhileEnd even [2,3,4,6] == [4,6]+ testGen "trim \" hello \" == \"hello\"" $ trim " hello " == "hello"+ testGen "trimStart \" hello \" == \"hello \"" $ trimStart " hello " == "hello "+ testGen "trimEnd \" hello \" == \" hello\"" $ trimEnd " hello " == " hello"+ testGen "\\s -> trim s == trimEnd (trimStart s)" $ \s -> trim s == trimEnd (trimStart s) testGen "lower \"This is A TEST\" == \"this is a test\"" $ lower "This is A TEST" == "this is a test" testGen "lower \"\" == \"\"" $ lower "" == ""+ testGen "upper \"This is A TEST\" == \"THIS IS A TEST\"" $ upper "This is A TEST" == "THIS IS A TEST"+ testGen "upper \"\" == \"\"" $ upper "" == ""+ testGen "sortOn fst [(3,\"z\"),(1,\"\"),(3,\"a\")] == [(1,\"\"),(3,\"z\"),(3,\"a\")]" $ sortOn fst [(3,"z"),(1,""),(3,"a")] == [(1,""),(3,"z"),(3,"a")]+ testGen "groupSort [(1,'t'),(3,'t'),(2,'e'),(2,'s')] == [(1,\"t\"),(2,\"es\"),(3,\"t\")]" $ groupSort [(1,'t'),(3,'t'),(2,'e'),(2,'s')] == [(1,"t"),(2,"es"),(3,"t")]+ testGen "\\xs -> map fst (groupSort xs) == sort (nub (map fst xs))" $ \xs -> map fst (groupSort xs) == sort (nub (map fst xs))+ testGen "\\xs -> concatMap snd (groupSort xs) == map snd (sortOn fst xs)" $ \xs -> concatMap snd (groupSort xs) == map snd (sortOn fst xs)+ testGen "merge \"ace\" \"bd\" == \"abcde\"" $ merge "ace" "bd" == "abcde"+ testGen "\\xs ys -> merge (sort xs) (sort ys) == sort (xs ++ ys)" $ \xs ys -> merge (sort xs) (sort ys) == sort (xs ++ ys)+ testGen "replace \"el\" \"_\" \"Hello Bella\" == \"H_lo B_la\"" $ replace "el" "_" "Hello Bella" == "H_lo B_la"+ testGen "replace \"el\" \"e\" \"Hello\" == \"Helo\"" $ replace "el" "e" "Hello" == "Helo"+ testGen "replace \"\" \"e\" \"Hello\" == undefined" $ erroneous $ replace "" "e" "Hello"+ testGen "\\xs ys -> not (null xs) ==> replace xs xs ys == ys" $ \xs ys -> not (null xs) ==> replace xs xs ys == ys testGen "breakEnd isLower \"youRE\" == (\"you\",\"RE\")" $ breakEnd isLower "youRE" == ("you","RE") testGen "breakEnd isLower \"youre\" == (\"youre\",\"\")" $ breakEnd isLower "youre" == ("youre","") testGen "breakEnd isLower \"YOURE\" == (\"\",\"YOURE\")" $ breakEnd isLower "YOURE" == ("","YOURE") testGen "spanEnd isUpper \"youRE\" == (\"you\",\"RE\")" $ spanEnd isUpper "youRE" == ("you","RE") testGen "spanEnd (not . isSpace) \"x y z\" == (\"x y \",\"z\")" $ spanEnd (not . isSpace) "x y z" == ("x y ","z") testGen "\\f xs-> spanEnd f xs == swap (both reverse (span f (reverse xs)))" $ \f xs-> spanEnd f xs == swap (both reverse (span f (reverse xs)))+ testGen "wordsBy (== ':') \"::xyz:abc::123::\" == [\"xyz\",\"abc\",\"123\"]" $ wordsBy (== ':') "::xyz:abc::123::" == ["xyz","abc","123"]+ testGen "\\s -> wordsBy isSpace s == words s" $ \s -> wordsBy isSpace s == words s+ testGen "linesBy (== ':') \"::xyz:abc::123::\" == [\"\",\"\",\"xyz\",\"abc\",\"\",\"123\",\"\"]" $ linesBy (== ':') "::xyz:abc::123::" == ["","","xyz","abc","","123",""]+ testGen "\\s -> linesBy (== '\\n') s == lines s" $ \s -> linesBy (== '\n') s == lines s+ testGen "linesBy (== ';') \"my;list;here;\" == [\"my\",\"list\",\"here\"]" $ linesBy (== ';') "my;list;here;" == ["my","list","here"]+ testGen "firstJust id [Nothing,Just 3] == Just 3" $ firstJust id [Nothing,Just 3] == Just 3+ testGen "firstJust id [Nothing,Nothing] == Nothing" $ firstJust id [Nothing,Nothing] == Nothing+ testGen "drop1 \"\" == \"\"" $ drop1 "" == ""+ testGen "drop1 \"test\" == \"est\"" $ drop1 "test" == "est"+ testGen "\\xs -> drop 1 xs == drop1 xs" $ \xs -> drop 1 xs == drop1 xs testGen "breakOn \"::\" \"a::b::c\" == (\"a\", \"::b::c\")" $ breakOn "::" "a::b::c" == ("a", "::b::c") testGen "breakOn \"/\" \"foobar\" == (\"foobar\", \"\")" $ breakOn "/" "foobar" == ("foobar", "") testGen "\\needle haystack -> let (prefix,match) = breakOn needle haystack in prefix ++ match == haystack" $ \needle haystack -> let (prefix,match) = breakOn needle haystack in prefix ++ match == haystack@@ -90,15 +130,29 @@ testGen "splitOn \"x\" \"\" == [\"\"]" $ splitOn "x" "" == [""] testGen "\\s x -> s /= \"\" ==> intercalate s (splitOn s x) == x" $ \s x -> s /= "" ==> intercalate s (splitOn s x) == x testGen "\\c x -> splitOn [c] x == split (==c) x" $ \c x -> splitOn [c] x == split (==c) x- testGen "split (=='a') \"aabbaca\" == [\"\",\"\",\"bb\",\"c\",\"\"]" $ split (=='a') "aabbaca" == ["","","bb","c",""]- testGen "split (=='a') \"\" == [\"\"]" $ split (=='a') "" == [""]+ testGen "split (== 'a') \"aabbaca\" == [\"\",\"\",\"bb\",\"c\",\"\"]" $ split (== 'a') "aabbaca" == ["","","bb","c",""]+ testGen "split (== 'a') \"\" == [\"\"]" $ split (== 'a') "" == [""]+ testGen "split (== ':') \"::xyz:abc::123::\" == [\"\",\"\",\"xyz\",\"abc\",\"\",\"123\",\"\",\"\"]" $ split (== ':') "::xyz:abc::123::" == ["","","xyz","abc","","123","",""]+ testGen "split (== ',') \"my,list,here\" == [\"my\",\"list\",\"here\"]" $ split (== ',') "my,list,here" == ["my","list","here"]+ testGen "dropWhileEnd isSpace \"ab cde \" == \"ab cde\"" $ dropWhileEnd isSpace "ab cde " == "ab cde"+ testGen "dropWhileEnd' isSpace \"ab cde \" == \"ab cde\"" $ dropWhileEnd' isSpace "ab cde " == "ab cde"+ testGen "last (dropWhileEnd even [undefined,3]) == undefined" $ erroneous $ last (dropWhileEnd even [undefined,3])+ testGen "last (dropWhileEnd' even [undefined,3]) == 3" $ last (dropWhileEnd' even [undefined,3]) == 3+ testGen "head (dropWhileEnd even (3:undefined)) == 3" $ head (dropWhileEnd even (3:undefined)) == 3+ testGen "head (dropWhileEnd' even (3:undefined)) == undefined" $ erroneous $ head (dropWhileEnd' even (3:undefined)) testGen "stripSuffix \"bar\" \"foobar\" == Just \"foo\"" $ stripSuffix "bar" "foobar" == Just "foo" testGen "stripSuffix \"\" \"baz\" == Just \"baz\"" $ stripSuffix "" "baz" == Just "baz" testGen "stripSuffix \"foo\" \"quux\" == Nothing" $ stripSuffix "foo" "quux" == Nothing testGen "chunksOf 3 \"my test\" == [\"my \",\"tes\",\"t\"]" $ chunksOf 3 "my test" == ["my ","tes","t"] testGen "chunksOf 3 \"mytest\" == [\"myt\",\"est\"]" $ chunksOf 3 "mytest" == ["myt","est"] testGen "chunksOf 8 \"\" == []" $ chunksOf 8 "" == []- testGen "chunksOf 0 \"test\" == undefined" $ erroneous $ chunksOf 0 "test" + testGen "chunksOf 0 \"test\" == undefined" $ erroneous $ chunksOf 0 "test"+ testGen "first succ (1,\"test\") == (2,\"test\")" $ first succ (1,"test") == (2,"test")+ testGen "second reverse (1,\"test\") == (1,\"tset\")" $ second reverse (1,"test") == (1,"tset")+ testGen "(succ *** reverse) (1,\"test\") == (2,\"tset\")" $ (succ *** reverse) (1,"test") == (2,"tset")+ testGen "(succ &&& pred) 1 == (2,0)" $ (succ &&& pred) 1 == (2,0)+ testGen "dupe 12 == (12, 12)" $ dupe 12 == (12, 12)+ testGen "both succ (1,2) == (2,3)" $ both succ (1,2) == (2,3) testGen "showDP 4 pi == \"3.1416\"" $ showDP 4 pi == "3.1416" testGen "showDP 0 pi == \"3\"" $ showDP 0 pi == "3" testGen "showDP 2 3 == \"3.00\"" $ showDP 2 3 == "3.00"