toolshed 0.12.0.0 → 0.13.0.0
raw patch · 28 files changed
+1197/−415 lines, 28 filesdep +Cabaldep +QuickCheckdep +arraynew-component:exe:toolshedPVP ok
version bump matches the API change (PVP)
Dependencies added: Cabal, QuickCheck, array, containers, directory, filepath, random
API changes (from Hackage documentation)
- ToolShed.ListPlus: chunk :: ChunkLength -> [a] -> [[a]]
- ToolShed.ListPlus: excise :: Int -> [a] -> [a]
- ToolShed.ListPlus: groupComparing :: Eq b => (a -> b) -> [a] -> [[a]]
- ToolShed.ListPlus: linearise :: [(a, a)] -> [a]
- ToolShed.ListPlus: merge :: Ord a => [a] -> [a] -> [a]
- ToolShed.ListPlus: mergeBy :: Ord a => (a -> a -> Ordering) -> [a] -> [a] -> [a]
- ToolShed.ListPlus: splitsLeftFrom :: Int -> [a] -> [Split a]
- ToolShed.ListPlus: splitsRightFrom :: Int -> [a] -> [Split a]
- ToolShed.ListPlus: takeUntil :: (a -> Bool) -> [a] -> [a]
- ToolShed.ListPlus: type ChunkLength = Int
- ToolShed.Pair: both :: (Bool, Bool) -> Bool
- ToolShed.Pair: mirror :: (a -> b) -> (a, a) -> (b, b)
- ToolShed.Pair: neither :: (Bool, Bool) -> Bool
- ToolShed.SelfValidate: instance SelfValidator a => SelfValidator [a]
- ToolShed.TimeAction: getCPUSeconds :: Fractional seconds => IO result -> IO (seconds, result)
- ToolShed.TimeAction: printCPUSeconds :: IO result -> IO result
- ToolShed.TimePure: getCPUSeconds :: (Fractional seconds, NFData expression) => expression -> IO (seconds, expression)
- ToolShed.TimePure: printCPUSeconds :: NFData expression => expression -> IO expression
- ToolShed.Unsafe: print' :: Show s => s -> s
- ToolShed.Unsafe: printShow :: Show s => String -> s -> s
+ ToolShed.Arbitrary.Array: instance (Ix i, Enum i, Arbitrary i, Arbitrary e) => Arbitrary (Array i e)
+ ToolShed.Arbitrary.ArrayElem: instance (Bounded i, Ix i, Enum i, Arbitrary e) => Arbitrary (Array i e)
+ ToolShed.Arbitrary.Map: instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map k v)
+ ToolShed.Arbitrary.Set: instance (Ord k, Arbitrary k) => Arbitrary (Set k)
+ ToolShed.Data.List: chunk :: ChunkLength -> [a] -> [[a]]
+ ToolShed.Data.List: equalityBy :: Eq b => (a -> b) -> a -> a -> Bool
+ ToolShed.Data.List: excise :: Int -> [a] -> [a]
+ ToolShed.Data.List: findConvergence :: Eq a => [a] -> a
+ ToolShed.Data.List: findConvergenceBy :: (a -> a -> Bool) -> [a] -> a
+ ToolShed.Data.List: gather :: (Foldable f, Ord a) => f a -> [[a]]
+ ToolShed.Data.List: gatherBy :: (Foldable f, Ord b) => (a -> b) -> f a -> [[a]]
+ ToolShed.Data.List: linearise :: [(a, a)] -> [a]
+ ToolShed.Data.List: merge :: Ord a => [a] -> [a] -> [a]
+ ToolShed.Data.List: mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
+ ToolShed.Data.List: permutations :: [[a]] -> [[a]]
+ ToolShed.Data.List: showListWith :: (Show token, Show element) => (token, token, token) -> [element] -> ShowS
+ ToolShed.Data.List: takeUntil :: (a -> Bool) -> [a] -> [a]
+ ToolShed.Data.List: type ChunkLength = Int
+ ToolShed.Data.List.Runlength: decode :: [Code a] -> [a]
+ ToolShed.Data.List.Runlength: encode :: Eq a => [a] -> [Code a]
+ ToolShed.Data.List.Runlength: getDatum :: Code a -> a
+ ToolShed.Data.List.Runlength: getLength :: Code a -> Int
+ ToolShed.Data.List.Runlength: type Code a = (Int, a)
+ ToolShed.Data.List.Splits: splitsLeftFrom :: Int -> [a] -> [Split a]
+ ToolShed.Data.List.Splits: splitsRightFrom :: Int -> [a] -> [Split a]
+ ToolShed.Data.Pair: mirror :: (a -> b) -> (a, a) -> (b, b)
+ ToolShed.Defaultable: instance (Defaultable a, Defaultable b) => Defaultable (a, b)
+ ToolShed.SelfValidate: extractErrors :: [(Bool, String)] -> [String]
+ ToolShed.SelfValidate: getErrors :: SelfValidator v => v -> [String]
+ ToolShed.SelfValidate: getFirstError :: SelfValidator v => v -> String
+ ToolShed.SelfValidate: instance (Ix index, SelfValidator element) => SelfValidator (Array index element)
+ ToolShed.SelfValidate: instance (SelfValidator a, SelfValidator b) => SelfValidator (a, b)
+ ToolShed.SelfValidate: instance SelfValidator v => SelfValidator (Map k v)
+ ToolShed.SelfValidate: instance SelfValidator v => SelfValidator (Maybe v)
+ ToolShed.SelfValidate: instance SelfValidator v => SelfValidator (Set v)
+ ToolShed.SelfValidate: instance SelfValidator v => SelfValidator [v]
+ ToolShed.System.File: fromFile :: Read a => FilePath -> SearchPath -> IO (LocatedData a)
+ ToolShed.System.File: getData :: LocatedData a -> a
+ ToolShed.System.File: getFile :: FilePath -> SearchPath -> IO (LocatedData String)
+ ToolShed.System.File: getFilePath :: LocatedData a -> FilePath
+ ToolShed.System.File: locate :: FilePath -> SearchPath -> IO [FilePath]
+ ToolShed.System.File: type LocatedData a = (FilePath, a)
+ ToolShed.System.File: type SearchPath = [FilePath]
+ ToolShed.System.Random: generateSelection :: RandomGen randomGen => randomGen -> [a] -> [a]
+ ToolShed.System.Random: generateSelectionFromBounded :: (RandomGen randomGen, Bounded a, Random a) => randomGen -> [a]
+ ToolShed.System.Random: randomGens :: RandomGen randomGen => randomGen -> [randomGen]
+ ToolShed.System.Random: select :: RandomGen randomGen => randomGen -> [a] -> a
+ ToolShed.System.Random: shuffle :: RandomGen randomGen => randomGen -> [a] -> [a]
+ ToolShed.System.TimeAction: getCPUSeconds :: Fractional seconds => IO result -> IO (seconds, result)
+ ToolShed.System.TimeAction: printCPUSeconds :: IO result -> IO result
+ ToolShed.System.TimePure: getCPUSeconds :: (Fractional seconds, NFData expression) => expression -> IO (seconds, expression)
+ ToolShed.System.TimePure: printCPUSeconds :: NFData expression => expression -> IO expression
- ToolShed.SelfValidate: class SelfValidator a
+ ToolShed.SelfValidate: class SelfValidator v where isValid = null . getErrors
- ToolShed.SelfValidate: isValid :: SelfValidator a => a -> Bool
+ ToolShed.SelfValidate: isValid :: SelfValidator v => v -> Bool
Files
- changelog +24/−0
- makefile +5/−5
- src/Main.hs +85/−0
- src/ToolShed/Arbitrary/Array.hs +52/−0
- src/ToolShed/Arbitrary/ArrayElem.hs +43/−0
- src/ToolShed/Arbitrary/Map.hs +39/−0
- src/ToolShed/Arbitrary/Set.hs +39/−0
- src/ToolShed/Data/List.hs +171/−0
- src/ToolShed/Data/List/Runlength.hs +55/−0
- src/ToolShed/Data/List/Splits.hs +66/−0
- src/ToolShed/Data/Pair.hs +37/−0
- src/ToolShed/Defaultable.hs +2/−0
- src/ToolShed/ListPlus.hs +0/−168
- src/ToolShed/Options.hs +2/−2
- src/ToolShed/Pair.hs +0/−44
- src/ToolShed/SelfValidate.hs +43/−5
- src/ToolShed/System/File.hs +96/−0
- src/ToolShed/System/Random.hs +78/−0
- src/ToolShed/System/TimeAction.hs +49/−0
- src/ToolShed/System/TimePure.hs +78/−0
- src/ToolShed/Test/Data/List.hs +69/−0
- src/ToolShed/Test/Data/List/Runlength.hs +36/−0
- src/ToolShed/Test/Data/List/Splits.hs +51/−0
- src/ToolShed/Test/QuickChecks.hs +37/−0
- src/ToolShed/TimeAction.hs +0/−49
- src/ToolShed/TimePure.hs +0/−78
- src/ToolShed/Unsafe.hs +0/−56
- toolshed.cabal +40/−8
changelog view
@@ -28,3 +28,27 @@ 0.12.0.0 * Added module "ToolShed.TimeAction" to measure the CPU-seconds required for an IO-action. * Added module "Pair".+0.12.0.1+ * Removed unnecessary type-context 'Ord' from 'ToolShed.ListPus.mergeBy' and reimplemented 'ToolShed.ListPus.merge' in terms of 'ToolShed.ListPus.mergeBy'.+ * Added 'ToolShed.ListPlus.runLengthEncode' and 'ToolShed.ListPlus.runLengthDecode'.+0.13.0.0+ * Removed module "ToolShed.Unsafe".+ * Renamed:+ "ToolShed.ListPlus" to "ToolShed.Data.List"+ "ToolShed.Pair" to "ToolShed.Data.Pair"+ "ToolShed.TimeAction" to "ToolShed.System.TimeAction"+ "ToolShed.TimePure" to "ToolShed.System.TimePure"+ * Migrated 'ToolShed.Data.List.RunLengthCode' and associated functions, into a new module "ToolShed.Data.List.Runlength".+ * Migrated 'ToolShed.Data.List.Splits' and associated functions, into a new module "ToolShed.Data.List.Splits".+ * Replaced function 'ToolShed.Data.List.groupComparing' with the more useful 'ToolShed.Data.List.equalityBy', which can be used with either 'Data.List.groupBy' or 'Data.List.nubBy'.+ * Added instances of 'ToolShed.SelfValidate.SelfValidator' for 'Data.Maybe.Maybe' '(,)', 'Data.Set.Set', 'Data.Map.Map' and 'Data.Array.IArray.Array'.+ * Added instance of 'ToolShed.Defaultable.Defaultable' for '(,)'.+ * Added functions 'ToolShed.Data.List.showListWith', 'ToolShed.Data.List.permutations', 'ToolShed.Data.List.gatherBy', 'ToolShed.Data.List.findConvergenceBy', 'ToolShed.Data.List.findConvergence', 'ToolShed.Data.List.gather'.+ * Added module "ToolShed.System.File".+ * Added module "ToolShed.System.Random".+ * Added method 'ToolShed.SelfValidate.getErrors', and supporting functions 'ToolShed.SelfValidate.getFirstErrors' and 'ToolShed.SelfValidate.extractErrors'.+ * Create module "ToolShed.Arbitrary.*".+ * Added "Main", "ToolShed.Test.QuickChecks" and "ToolShed.Test.Data.List" to facilitate testing.+ * Removed 'ToolShed.Data.Pair.both'.+ * Replaced "System" with "System.Environment" and "System.Exit".+ * Removed dependency on "haskell98".
makefile view
@@ -1,18 +1,18 @@ # Copyright (C) 2010 Dr. Alistair Ward-# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version.-# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details.-# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>.- + .PHONY: all build check clean configure copy haddock help hlint install prof sdist all: install@@ -50,7 +50,7 @@ clean: runhaskell Setup.hs $@- find src -type f \( -name '*.o' -o -name '*.hi' \) -delete+ find src -type f \( -name '*.hc' -o -name '*.hcr' -o -name '*.hi' -o -name '*.o' \) -delete help: @grep '^[a-zA-Z].*:' makefile | sed -e 's/:.*//'
+ src/Main.hs view
@@ -0,0 +1,85 @@+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]++ * Contains the entry-point to the program.++ * Facilitates testing.+-}++module Main(+-- * Types+-- ** Type-synonyms+-- CommandLineAction,+-- * Functions+ main+) where++import qualified Data.List+import qualified Data.Version+import qualified Distribution.Package+import qualified Distribution.Text+import qualified Distribution.Version+import qualified Paths_toolshed as Paths --Either local stub, or package-instance autogenerated by 'Setup.hs build'.+import qualified System.Console.GetOpt as G+import qualified System.Environment+import qualified System.Exit+import qualified System.IO+import qualified System.IO.Error+import qualified ToolShed.Defaultable+import qualified ToolShed.Test.QuickChecks++-- | Used to thread user-defined command-line options, though the list of functions which implement them.+type CommandLineAction = () -> IO () --Supplied as the type-argument to 'G.OptDescr'.++-- | Parses the command-line arguments, to determine 'Test.CommandOptions.CommandOptions'.+main :: IO ()+main = do+ progName <- System.Environment.getProgName+ args <- System.Environment.getArgs++ let+ usage :: String+ usage = "Usage:\t" ++ G.usageInfo progName optDescrList++--Define the command-line options, and the 'CommandLineAction's used to handle them.+ optDescrList :: [G.OptDescr CommandLineAction]+ optDescrList = [+-- String [String] (G.ArgDescr CommandLineAction) String+ G.Option "?" ["help"] (G.NoArg $ const printUsage) "Display this help-text & then exit.",+ G.Option "" ["version"] (G.NoArg $ const printVersion) "Print version-information & then exit.",+ G.Option "q" ["runQuickChecks"] (G.NoArg $ const runQuickChecks) "Run Quick-checks using arbitrary data & then exit."+ ] where+ printVersion, printUsage, runQuickChecks :: IO ()+ printVersion = System.IO.hPutStrLn System.IO.stderr (Distribution.Text.display packageIdentifier ++ "\n\nCopyright (C) 2011 Dr. Alistair Ward.\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions.\n\nWritten by Dr. Alistair Ward.") >> System.Exit.exitWith System.Exit.ExitSuccess where+ packageIdentifier :: Distribution.Package.PackageIdentifier+ packageIdentifier = Distribution.Package.PackageIdentifier {+ Distribution.Package.pkgName = Distribution.Package.PackageName "toolshed",+ Distribution.Package.pkgVersion = Distribution.Version.Version (Data.Version.versionBranch Paths.version) []+ }++ printUsage = System.IO.hPutStrLn System.IO.stderr usage >> System.Exit.exitWith System.Exit.ExitSuccess+ runQuickChecks = ToolShed.Test.QuickChecks.run >> System.Exit.exitWith System.Exit.ExitSuccess++-- G.getOpt :: G.ArgOrder CommandLineAction -> [G.OptDescr Action] -> [String] -> ([Action], [String], [String])+ case G.getOpt G.RequireOrder optDescrList args of+ (commandLineActions, _, []) -> Data.List.foldl' (>>=) (return {-to IO-monad-} ()) commandLineActions >> System.Exit.exitWith System.Exit.ExitSuccess+ (_, _, errors) -> System.IO.Error.ioError . System.IO.Error.userError $ concat errors ++ usage --Throw.+
+ src/ToolShed/Arbitrary/Array.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Implements 'Test.QuickCheck.Arbitrary' for 'Data.Array.IArray.Array'.++ [@EXAMPLE@] @Test.QuickCheck.sample (Test.QuickCheck.arbitrary :: Test.QuickCheck.Gen.Gen (Data.Array.IArray.Array Char Int))@++ [@CAVEAT@] An array with arbitrary indices isn't very useful.+-}++module ToolShed.Arbitrary.Array() where++import Control.Applicative((<$>))+import qualified Data.Array.IArray+import qualified Data.Tuple+import qualified Test.QuickCheck++instance (Data.Array.IArray.Ix i, Enum i, Test.QuickCheck.Arbitrary i, Test.QuickCheck.Arbitrary e) => Test.QuickCheck.Arbitrary (Data.Array.IArray.Array i e) where+ arbitrary = do+ pair <- Test.QuickCheck.arbitrary {-(i, i)-}++ let+ bounds@(lower, upper)+ | uncurry (>) pair = Data.Tuple.swap pair+ | otherwise = pair++ indices = [lower .. upper]++ Data.Array.IArray.array bounds . zip indices <$> Test.QuickCheck.vector (length indices)++#if !(MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif+
+ src/ToolShed/Arbitrary/ArrayElem.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]++ Implements 'Test.QuickCheck.Arbitrary' for 'Data.Array.IArray.Array',+ where the array-index is required to be a /bounded enumerable/ type.++ [@EXAMPLE@] @Test.QuickCheck.sample (Test.QuickCheck.arbitrary :: Test.QuickCheck.Gen.Gen (Data.Array.IArray.Array Data.Int.Int8 Int))@+-}++module ToolShed.Arbitrary.ArrayElem() where++import Control.Applicative((<$>))+import qualified Data.Array.IArray+import qualified Test.QuickCheck++instance (Bounded i, Data.Array.IArray.Ix i, Enum i, Test.QuickCheck.Arbitrary e) => Test.QuickCheck.Arbitrary (Data.Array.IArray.Array i e) where+ arbitrary = Data.Array.IArray.array bounds . zip [minBound .. maxBound] <$> Test.QuickCheck.vector (succ $ fromEnum (snd bounds) - fromEnum (fst bounds)) where+ bounds = (minBound, maxBound)++#if !(MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif+
+ src/ToolShed/Arbitrary/Map.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Implements 'Test.QuickCheck.Arbitrary' for 'Data.Map.Map'.++ [@EXAMPLE@] @Test.QuickCheck.sample (Test.QuickCheck.arbitrary :: Test.QuickCheck.Gen.Gen (Data.Map.Map Char Int))@+-}++module ToolShed.Arbitrary.Map() where++import Control.Applicative((<$>))+import qualified Data.Map+import qualified Test.QuickCheck++instance (Ord k, Test.QuickCheck.Arbitrary k, Test.QuickCheck.Arbitrary v) => Test.QuickCheck.Arbitrary (Data.Map.Map k v) where+ arbitrary = Data.Map.fromList <$> Test.QuickCheck.arbitrary {-[(k, v)]-}++#if !(MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif+
+ src/ToolShed/Arbitrary/Set.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Implements 'Test.QuickCheck.Arbitrary' for 'Data.Set.Set'.++ [@EXAMPLE@] @Test.QuickCheck.sample (Test.QuickCheck.arbitrary :: Test.QuickCheck.Gen.Gen (Data.Set.Set Int))@+-}++module ToolShed.Arbitrary.Set() where++import Control.Applicative((<$>))+import qualified Data.Set+import qualified Test.QuickCheck++instance (Ord k, Test.QuickCheck.Arbitrary k) => Test.QuickCheck.Arbitrary (Data.Set.Set k) where+ arbitrary = Data.Set.fromList <$> Test.QuickCheck.arbitrary {-[k]-}++#if !(MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif+
+ src/ToolShed/Data/List.hs view
@@ -0,0 +1,171 @@+{-+ Copyright (C) 2010 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Miscellaneous polymorphic list-operations.+-}++module ToolShed.Data.List(+-- * Types+-- ** Type-synonyms+ ChunkLength,+-- * Functions+ chunk,+ excise,+ equalityBy,+ findConvergence,+ findConvergenceBy,+ gather,+ gatherBy,+ linearise,+ merge,+ mergeBy,+ permutations,+ takeUntil,+ showListWith,+-- showListWithChar+-- showListWithString+) where++import qualified Control.Arrow+import qualified Data.Foldable+import qualified Data.List+import qualified Data.Map+--import qualified Data.Ord++-- | The length of the chunks into which a list is split.+type ChunkLength = Int++{- |+ * Splits a list into length-@size@ pieces, where @(size >= 0)@.++ * The last chunk will be shorter, if @n@ isn't an aliquot part of the input list-length.++ * If @(size == 0)@, the resulting list will be an infinite sequence of null lists.++ * CAVEAT: a similar function is available in the module /Data.List.Split/, though this one checks for @(size < 0)@.+-}+chunk+ :: ChunkLength+ -> [a] -- ^ The polymorphic input list to be chunked.+ -> [[a]]+chunk size list+ | size < 0 = error $ "ToolShed.Data.List.chunk:\tnegative chunk-size=" ++ show size+ | otherwise = chunk' list+ where+ chunk' :: [a] -> [[a]]+ chunk' [] = []+ chunk' a = uncurry (:) . Control.Arrow.second chunk' $ splitAt size a++-- | Remove the single indexed element from the list.+excise+ :: Int -- ^ The index.+ -> [a] -- ^ The polymorphic input list.+ -> [a] -- ^ The same list, with the indexed element removed.+excise 0 = tail --Just for efficiency.+excise i = uncurry (++) . Control.Arrow.second tail . splitAt i++-- | A convenient way to compose the function-parameter required by 'Data.List.groupBy' or 'Data.List.nubBy'.+equalityBy :: Eq b => (a -> b) -> a -> a -> Bool+equalityBy f x y = f x == f y++-- | Take the first element from the list, which compares equal according to the specified predicate, with the subsequent element.+findConvergenceBy :: (a -> a -> Bool) -> [a] -> a+findConvergenceBy predicate l = fst . head . dropWhile (not . uncurry predicate) . zip l $ tail l++-- | A specific instance of 'findConvergenceBy'.+findConvergence :: Eq a => [a] -> a+findConvergence = findConvergenceBy (==)++{- |+ * Group equal (though not necessarily adjacent) elements, according to the specified comparator.++ * The groups are returned in ascending order, whilst their elements remain in their original order.+-}+gatherBy :: (Data.Foldable.Foldable f, Ord b) => (a -> b) -> f a -> [[a]]+gatherBy f = Data.Map.elems . Data.Foldable.foldr (\x -> Data.Map.insertWith (++) (f x) [x]) Data.Map.empty++-- | A specific instance of 'gatherBy'.+gather :: (Data.Foldable.Foldable f, Ord a) => f a -> [[a]]+gather = gatherBy id++-- | Converts a list of /Pairs/, into a narrower list.+linearise :: [(a, a)] -> [a]+linearise [] = []+linearise ((l, r) : remainder) = l : r : linearise remainder --Recurse.++{- |+ * Merge two sorted lists, according to the specified order, to product a single sorted list.++ * The merge-process is /stable/, in that where items from each list are equal, they remain in the original order.++ * CAVEAT: duplicates are preserved.+-}+mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]+mergeBy cmp = slave where+ slave l@(x : xs) r@(y : ys)+ | x `cmp` y == GT = y : slave l ys+ | otherwise = x : slave xs r+ slave [] r = r+ slave l [] = l++-- | A specific instance of 'mergeBy'.+merge :: Ord a => [a] -> [a] -> [a]+merge = mergeBy compare++{- |+ * The list of all permutations, generated by selecting any one datum from each sub-list in turn, from the specified list of lists.++ * Taken from <http://www.haskell.org/pipermail/haskell-cafe/2006-June/015975.html>.+-}+permutations :: [[a]] -> [[a]]+permutations (xs : xss) = [ x : xs' | x <- xs, xs' <- permutations xss ]+permutations [] = [[]]++{- |+ * Take until the specified predicate is satisfied; /including/ the item which satisfied it.++ * NB: @takeWhile (not . test)@ would return one fewer item.+-}+takeUntil+ :: (a -> Bool) -- ^ Predicate, used to determine the last item taken.+ -> [a] -- ^ The polymorphic input list.+ -> [a]+takeUntil predicate = takeUntil' where+ takeUntil' (x : xs) = x {-take regardless-} : if predicate x then [] else takeUntil' xs+ takeUntil' _ = []++-- | Show a list, delimited by the specified tokens.+showListWith :: (Show token, Show element)+ => (token, token, token) -- ^ (Start-delimiter, separator, end-delimiter)+ -> [element] -- ^ The polymorphic list to show.+ -> ShowS+showListWith (startDelimiter, separator, endDelimiter) = foldr (.) (shows endDelimiter) . (shows startDelimiter :) . Data.List.intersperse (shows separator) . map shows++{-# NOINLINE showListWith #-}+{-# RULES "showListWith/Char" showListWith = showListWithChar #-}+{-# RULES "showListWith/String" showListWith = showListWithString #-}++-- | A specialisation of 'showListWith'.+showListWithChar :: Show element => (Char, Char, Char) -> [element] -> ShowS+showListWithChar (startDelimiter, separator, endDelimiter) = foldr (.) (showChar endDelimiter) . (showChar startDelimiter :) . Data.List.intersperse (showChar separator) . map shows++-- | A specialisation of 'showListWith'.+showListWithString :: Show element => (String, String, String) -> [element] -> ShowS+showListWithString (startDelimiter, separator, endDelimiter) = foldr (.) (showString endDelimiter) . (showString startDelimiter :) . Data.List.intersperse (showString separator) . map shows+
+ src/ToolShed/Data/List/Runlength.hs view
@@ -0,0 +1,55 @@+{-+ Copyright (C) 2010 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Run-length encoder and decoder.+-}++module ToolShed.Data.List.Runlength(+-- * Types+-- ** Type-synonyms+ Code,+-- * Functions+ encode,+ decode,+-- ** Accessors+ getLength,+ getDatum+) where++import Control.Arrow((&&&))+import qualified Data.List++-- | Describes the number of consecutive equal items in a list.+type Code a = (Int, a)++-- | Accessor.+getLength :: Code a -> Int+getLength = fst++-- | Accessor.+getDatum :: Code a -> a+getDatum = snd++-- | /Run-length/ encodes the specified list.+encode :: Eq a => [a] -> [Code a]+encode = map (length &&& head) . Data.List.group++-- | Performs /run-length/ decoding to retrieve the original unencoded list.+decode :: [Code a] -> [a]+decode = concatMap (uncurry replicate)
+ src/ToolShed/Data/List/Splits.hs view
@@ -0,0 +1,66 @@+{-+ Copyright (C) 2010 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]+-}++module ToolShed.Data.List.Splits(+-- * Types+-- ** Type-synonyms+-- Split,+-- * Functions+-- splitsFrom,+ splitsLeftFrom,+ splitsRightFrom+) where++-- | The polymorphic pair, resulting from splitting a list of arbitrary type.+type Split a = ([a] {-left list-}, [a] {-right list-})++-- | Use the specified transformation, to generate a list of 'Split's, from the initial one.+splitsFrom+ :: (Split a -> Split a) -- ^ The function used to transform one /split/ into the next.+ -> Int -- ^ Index.+ -> [a] -- ^ The polymorphic input list from which the /splits/ are generated.+ -> [Split a] -- ^ The list of all required splits of the single input list.+splitsFrom transformation i+ | i < 0 = error $ "ToolShed.Data.List.Splits.splitsFrom:\tnegative starting-index; " ++ show i+ | otherwise = iterate transformation . splitAt i++{- |+ * Create the set of all 'Split's, migrating left from the specified location.++ * CAVEAT: 'init' fails when 'fst' has been reduced to null.+-}+splitsLeftFrom+ :: Int -- ^ Index.+ -> [a] -- ^ The polymorphic input list from which the /splits/ are generated, as the index is stepped left+ -> [Split a] -- ^ The list of all required splits of the single input list.+splitsLeftFrom = splitsFrom (\(l, r) -> (init l, last l : r))++{- |+ * Create the set of all 'Split's, migrating right from the specified location.++ * CAVEAT: pattern-match against @ : @ fails, when 'snd' has been reduced to 'null'.+-}+splitsRightFrom+ :: Int -- ^ Index.+ -> [a] -- ^ The polymorphic input list from which the /splits/ are generated, as the index is stepped right.+ -> [Split a] -- ^ The list of all required splits of the single input list.+splitsRightFrom = splitsFrom (\(l, r : rs) -> (l ++ [r], rs))
+ src/ToolShed/Data/Pair.hs view
@@ -0,0 +1,37 @@+{-+ Copyright (C) 2010 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Miscellaneous operations on Pairs.+-}++module ToolShed.Data.Pair(+-- * Functions+ mirror+) where++import Control.Arrow((***))++{- |+ * Apply the same transformation to both halves of a /Pair/.++ * CAVEAT: the pair is required to have identical types.+-}+mirror :: (a -> b) -> (a, a) -> (b, b)+mirror f = f *** f+
src/ToolShed/Defaultable.hs view
@@ -29,3 +29,5 @@ class Defaultable a where defaultValue :: a -- ^ The default value of the data-type. +instance (Defaultable a, Defaultable b) => Defaultable (a, b) where+ defaultValue = (defaultValue, defaultValue)
− src/ToolShed/ListPlus.hs
@@ -1,168 +0,0 @@-{-- Copyright (C) 2010 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Miscellaneous polymorphic list-operations.--}--module ToolShed.ListPlus(--- * Types--- ** Type-synonyms- ChunkLength,--- Split,--- * Functions- chunk,- excise,- groupComparing,- linearise,- merge,- mergeBy,--- splitsFrom,- splitsLeftFrom,- splitsRightFrom,- takeUntil-) where--import qualified Control.Arrow-import qualified Data.List---- | The length of the chunks into which a list is split.-type ChunkLength = Int--{- |- * Splits a list into length-@size@ pieces, where @(size >= 0)@.-- * The last chunk will be shorter, if @n@ isn't an aliquot part of the input list-length.-- * If @(size == 0)@, the resulting list will be an infinite sequence of null lists.-- * CAVEAT: a similar function is available in the module /Data.List.Split/, though this one checks for @(size < 0)@.--}-chunk ::- ChunkLength- -> [a] -- ^ The polymorphic input list to be chunked.- -> [[a]]-chunk size list- | size < 0 = error $ "ToolShed.ListPlus.chunk:\tnegative chunk-size=" ++ show size- | otherwise = chunk' list- where- chunk' :: [a] -> [[a]]- chunk' [] = []- chunk' a = uncurry (:) . Control.Arrow.second chunk' $ splitAt size a---- | Remove the single indexed element from the list.-excise ::- Int -- ^ The index.- -> [a] -- ^ The polymorphic input list.- -> [a] -- ^ The same list, with the indexed element removed.-excise 0 = tail --Just for efficiency.-excise i = uncurry (++) . Control.Arrow.second tail . splitAt i--{- |- * Much like 'Data.List.GroupBy', but where the normal binary predicate,- is composed from equality, after the same unary translation-function has been applied to both list-elements.-- * cf. 'GHC.Exts.groupWith', which uses the function parameter to both sort and group.--}-groupComparing :: Eq b- => (a -> b) -- ^ Translates elements from the list, prior to testing the translated values for equality.- -> [a] -- ^ The polymorphic input list to group.- -> [[a]] -- ^ The same list split into chunks of the required length.-groupComparing f = Data.List.groupBy (\a b -> f a == f b)---- | Converts a list of /Pairs/, into a narrower list.-linearise :: [(a, a)] -> [a]-linearise [] = []-linearise ((l, r) : remainder) = l : r : linearise remainder --Recurse--{- |- * Merge two sorted lists, to product a single sorted list.-- * The merge-process is /stable/, in that where items from each list are equal, they remain in the original order.--}-merge :: Ord a => [a] -> [a] -> [a]-merge [] r = r-merge l [] = l-merge l@(x : xs) r@(y : ys)- | x > y = y : merge l ys- | otherwise = x : merge xs r--{- |- * Merge two sorted lists, according to the specified order, to product a single sorted list.-- * The merge-process is /stable/, in that where items from each list are equal, they remain in the original order.--}-mergeBy :: Ord a => (a -> a -> Ordering) -> [a] -> [a] -> [a]-mergeBy _ [] r = r-mergeBy _ l [] = l-mergeBy cmp l@(x : xs) r@(y : ys)- | x `cmp` y == GT = y : mergeBy cmp l ys- | otherwise = x : mergeBy cmp xs r ---- | The polymorphic pair, resulting from splitting a list of arbitrary type.-type Split a = ([a] {-left list-}, [a] {-right list-})---- | Use the specified transformation, to generate a list of 'Split's, from the initial one.-splitsFrom ::- (Split a -> Split a) -- ^ The function used to transform one /split/ into the next.- -> Int -- ^ Index.- -> [a] -- ^ The polymorphic input list from which the /splits/ are generated.- -> [Split a] -- ^ The list of all required splits of the single input list.-splitsFrom transformation i = iterate transformation . splitAt i--{- |- * Create the set of all 'Split's, migrating left from the specified location.-- * CAVEAT: 'init' fails when 'fst' has been reduced to null.--}-splitsLeftFrom ::- Int -- ^ Index.- -> [a] -- ^ The polymorphic input list from which the /splits/ are generated, as the index is stepped left- -> [Split a] -- ^ The list of all required splits of the single input list.-splitsLeftFrom start- | start < 0 = error $ "ToolShed.ListPlus.splitsLeftFrom:\tnegative starting-index; " ++ show start- | otherwise = splitsFrom (\(l, r) -> (init l, last l : r)) start--{- |- * Create the set of all 'Split's, migrating right from the specified location.-- * CAVEAT: pattern-match against @ : @ fails, when 'snd' has been reduced to null.--}-splitsRightFrom ::- Int -- ^ Index.- -> [a] -- ^ The polymorphic input list from which the /splits/ are generated, as the index is stepped right.- -> [Split a] -- ^ The list of all required splits of the single input list.-splitsRightFrom start- | start < 0 = error $ "ToolShed.ListPlus.splitsRightFrom:\tnegative starting-index; " ++ show start- | otherwise = splitsFrom (\(l, r : rs) -> (l ++ [r], rs)) start--{- |- * Take until the specified predicate is satisfied; /including/ the item which satisfied it.-- * NB: @takeWhile (not . test)@ would return one fewer item.--}-takeUntil ::- (a -> Bool) -- ^ Predicate, used to determine the last item taken.- -> [a] -- ^ The polymorphic input list.- -> [a]-takeUntil predicate = takeUntil' where- takeUntil' (x : xs) = x {-take regardless-} : if predicate x- then []- else takeUntil' xs- takeUntil' _ = []-
src/ToolShed/Options.hs view
@@ -25,8 +25,8 @@ Options(..) ) where -import ToolShed.Defaultable as Defaultable+import qualified ToolShed.Defaultable -- | Similar to the class 'Text.Regex.Base.RegexLike.RegexOptions'.-class Defaultable a => Options a where+class ToolShed.Defaultable.Defaultable a => Options a where blankValue :: a -- ^ The /undefined/ state of the data-type, which may be literal, but could alternatively be a logical starting value.
− src/ToolShed/Pair.hs
@@ -1,44 +0,0 @@-{-- Copyright (C) 2010 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Miscellaneous operations on Pairs.--}--module ToolShed.Pair(--- * Functions- mirror,--- ** Predicates- both,- neither-) where--import Control.Arrow((***))---- | Apply the same transformation to both halves of a /Pair/.-mirror :: (a -> b) -> (a, a) -> (b, b)-mirror f = f *** f---- | 'True' if both halves of the /Pair/ are.-both :: (Bool, Bool) -> Bool-both = uncurry (&&)---- | 'True' if neither half of the /Pair/ is.-neither :: (Bool, Bool) -> Bool-neither = not . uncurry (||)-
src/ToolShed/SelfValidate.hs view
@@ -22,13 +22,51 @@ module ToolShed.SelfValidate( -- * Type-classes- SelfValidator(..)+ SelfValidator(..),+-- ** Functions+ getFirstError,+ extractErrors ) where +import qualified Data.Array.IArray+import qualified Data.Map+import qualified Data.Set+ -- | The interface to which data which can self-validate should conform.-class SelfValidator a where- isValid :: a -> Bool -- ^ The data which implements this interface should return 'True' if internally consistent.+class SelfValidator v where+ getErrors :: v -> [String] -- ^ Return either null, or the reasons why the data is invalid. -instance SelfValidator a => SelfValidator [a] where- isValid = all isValid --NB: a null list is presumed to be OK.+ isValid :: v -> Bool -- ^ The data which implements this interface should return 'True' if internally consistent.+ isValid = null . getErrors --Default implementation.++instance (SelfValidator v) => SelfValidator (Maybe v) where+ getErrors (Just v) = getErrors v+ getErrors _ = []++instance (SelfValidator a, SelfValidator b) => SelfValidator (a, b) where+ getErrors (a, b) = getErrors a ++ getErrors b++instance SelfValidator v => SelfValidator [v] where+ getErrors = concatMap getErrors++instance SelfValidator v => SelfValidator (Data.Set.Set v) where+ getErrors = Data.Set.fold ((++) . getErrors) []++instance SelfValidator v => SelfValidator (Data.Map.Map k v) where+ getErrors = Data.Map.fold ((++) . getErrors) []++instance (Data.Array.IArray.Ix index, SelfValidator element) => SelfValidator (Data.Array.IArray.Array index element) where+ getErrors = concatMap getErrors . Data.Array.IArray.elems++-- | Returns the first error only (so only call on failure of 'isValid'), since subsequent tests may be based on invalid data.+getFirstError :: SelfValidator v => v -> String+getFirstError selfValidator+ | null errors = error "ToolShed.SelfValidate.getFirstError:\tzero errors ?!"+ | otherwise = head errors+ where+ errors = getErrors selfValidator++-- | Filters failed tests amongst those specified.+extractErrors :: [(Bool, String)] -> [String]+extractErrors = map snd . filter fst
+ src/ToolShed/System/File.hs view
@@ -0,0 +1,96 @@++{-+ Copyright (C) 2011 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] File-operations.+-}++module ToolShed.System.File(+-- * Types+-- ** Type-synonyms+ SearchPath,+ LocatedData,+-- * Functions+ locate,+ getFile,+ fromFile,+-- ** Accessors+ getFilePath,+ getData+) where++import Control.Applicative((<$>))+import qualified Control.Exception+import qualified Control.Monad+import qualified Data.List+import qualified System.Directory+import qualified System.FilePath+import System.FilePath((</>))+import qualified System.IO.Error++-- | The ordered sequence of directories, searched for a file.+type SearchPath = [System.FilePath.FilePath]++{- |+ * Locates all instances of the specified file, on the /search-path/.++ * CAVEAT: invalid or duplicate paths are silently removed.++ * CAVEAT: doesn't perform file-globbing.+-}+locate :: System.FilePath.FilePath -> SearchPath -> IO [System.FilePath.FilePath]+locate fileName = Control.Monad.filterM System.Directory.doesFileExist . filter System.FilePath.isValid . Data.List.nubBy System.FilePath.equalFilePath . map (System.FilePath.normalise . (</> fileName))++-- | A file-path, and the contents read from it.+type LocatedData a = (System.FilePath.FilePath, a)++-- | Accessor.+getFilePath :: LocatedData a -> System.FilePath.FilePath+getFilePath = fst++-- | Accessor.+getData :: LocatedData a -> a+getData = snd++{- |+ Traverse the /search-path/, looking for matching instances of the specified file-name,+ and either throw an exception, or return a pair composed from the path to the first matching file, together with its contents.+-}+getFile :: System.FilePath.FilePath -> SearchPath -> IO (LocatedData String)+getFile fileName directories = do+ filePaths <- locate fileName directories++ if null filePaths+ then Control.Exception.throw . System.IO.Error.mkIOError System.IO.Error.doesNotExistErrorType ("Can't find in " ++ show directories) Nothing $ Just fileName+ else {-located-} let+ filePath = head filePaths --Discard any subsequent paths.+ in (,) filePath <$> readFile filePath++{- |+ * Returns the polymorphic data, read from the first matching file on the /search-path/, along with the path from which it was read.++ * Returns an error on failure to parse the contents of the first matching file found on the /search-path/.+-}+fromFile :: Read a => System.FilePath.FilePath -> SearchPath -> IO (LocatedData a)+fromFile fileName directories = do+ (filePath, fileContents) <- getFile fileName directories++ case reads fileContents of+ [(x, _)] -> return {-to IO-monad-} (filePath, x) --CAVEAT: discards any unconsumed text.+ _ -> error $ "ToolShed.System.File.fromFile:\tfailed to parse file='" ++ filePath ++ "'"
+ src/ToolShed/System/Random.hs view
@@ -0,0 +1,78 @@++{-+ Copyright (C) 2011 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Utilities related to random-numbers.+-}++module ToolShed.System.Random(+-- * Functions+ randomGens,+ shuffle,+ generateSelection,+ generateSelectionFromBounded,+ select+) where++import qualified Control.Arrow+import qualified Data.List+import qualified Data.Ord+import qualified System.Random++-- | Constructs an infinite list of independent random-generators.+randomGens :: System.Random.RandomGen randomGen => randomGen -> [randomGen]+randomGens = uncurry (:) . Control.Arrow.second randomGens . System.Random.split++{- |+ * Shuffles the specified finite list.++ * The resulting list has the same length and constituents as the original; only the order has changed.++ * CAVEAT: the implementation /zips/ a list of integers, with the specified polymorphic list, then sorts it,+ but when identical random integers are generated, the sort-algorithm being /stable/ always return the corresponding items in their original order.+ The shuffle is therefore imperfect,+ but on a /64-bit/ machine, it would need such a large list of items, for the probability of randomly generating two identical integers, to be significant,+ that /sort/ probably wouldn't return in a reasonable time anyway.+ Ideally, it would be amended to use an /unstable/ sort-algorithm.+-}+shuffle :: System.Random.RandomGen randomGen => randomGen -> [a] -> [a]+shuffle randomGen = map snd . Data.List.sortBy (Data.Ord.comparing fst) . zip (System.Random.randoms randomGen :: [Int])++{- |+ * Generate an infinite list of items, each randomly selected from the specified finite list.++ * CAVEAT: because the selections are made non-destructively, duplicates may be returned; cf. 'shuffle'.+-}+generateSelection :: System.Random.RandomGen randomGen => randomGen -> [a] -> [a]+generateSelection randomGen l = map (l !!) $ System.Random.randomRs (0, pred $ length l) randomGen++-- | Return a random element from the specified list.+select :: System.Random.RandomGen randomGen => randomGen -> [a] -> a+select randomGen = head . generateSelection randomGen++{- |+ * Generate an infinite list of items, each randomly selected, from the specified finite list of bounded items.++ * Because the selections are made non-destructively, duplicates may be returned.++ * E.g. @ (generateSelectionFromBounded `fmap` System.Random.getStdGen) :: IO [Bool] @.+-}+generateSelectionFromBounded :: (System.Random.RandomGen randomGen, Bounded a, System.Random.Random a) => randomGen -> [a]+generateSelectionFromBounded = System.Random.randomRs (minBound, maxBound)+
+ src/ToolShed/System/TimeAction.hs view
@@ -0,0 +1,49 @@+{-+ Copyright (C) 2011 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Determines the CPU-time, required to evaluate the specified IO-action.++-}++module ToolShed.System.TimeAction(+-- * Functions+ getCPUSeconds,+ printCPUSeconds+) where++import qualified System.CPUTime+import qualified System.IO++-- | Time the specified IO-action, returning the required number of CPU-seconds and the result, as a 'Pair'.+getCPUSeconds :: Fractional seconds => IO result -> IO (seconds, result)+getCPUSeconds action = do+ startTime <- System.CPUTime.getCPUTime+ result <- action+ endTime <- System.CPUTime.getCPUTime++ return (fromInteger (endTime - startTime) / 1e12 {-convert from pico-seconds-}, result)++-- | Print the time required by the specified IO-action.+printCPUSeconds :: IO result -> IO result+printCPUSeconds action = do+ (cpuSeconds, result) <- getCPUSeconds action++ System.IO.hPutStrLn System.IO.stderr $ "CPU-seconds:\t" ++ show (cpuSeconds :: Double)++ return {-to IO-monad-} result
+ src/ToolShed/System/TimePure.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE CPP #-}+{-+ Copyright (C) 2010 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Determines the CPU-time, required to evaluate the specified pure expression.++-}++module ToolShed.System.TimePure(+-- * Functions+ getCPUSeconds,+ printCPUSeconds+) where++import qualified System.CPUTime+import qualified System.IO++{-+ The function used to force evaluation of the pure expression 'rnf',+ and the type-class which the polymorphic data must implement 'NFData',+ has been relocated from module "Control.Parallel.Strategies" to "Control.DeepSeq".+-}+#ifdef HAVE_DEEPSEQ+import Control.DeepSeq(NFData, deepseq)+#else+import Control.Parallel(pseq)+import Control.Parallel.Strategies(NFData, rnf)+#endif++{- |+ * Time the specified pure expression, returning the required number of CPU-seconds and the result, as a 'Pair'.++ * CAVEAT: as a side-effect, the expression is /deep/ evaluated.+-}+getCPUSeconds :: (Fractional seconds, NFData expression)+ => expression -- ^ Arbitrary polymorphic expression.+ -> IO (seconds, expression) -- ^ The original expression, tagged with the CPU-seconds taken.+getCPUSeconds expression = do+ start <- System.CPUTime.getCPUTime+ end <-+#ifdef HAVE_DEEPSEQ+ expression `deepseq`+#else+ rnf expression `pseq`+#endif+ System.CPUTime.getCPUTime++ return {-to IO-monad-} (fromInteger (end - start) / 1e12 {-convert from pico-seconds-}, expression)++{- |+ * Print the time required by the specified pure expression.++ * CAVEAT: as a side-effect, the expression is /deep/ evaluated.+-}+printCPUSeconds :: NFData expression => expression -> IO expression+printCPUSeconds expression = do+ (cpuSeconds, result) <- getCPUSeconds expression++ System.IO.hPutStrLn System.IO.stderr $ "CPU-seconds:\t" ++ show (cpuSeconds :: Double)++ return {-to IO-monad-} result+
+ src/ToolShed/Test/Data/List.hs view
@@ -0,0 +1,69 @@+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Defines /QuickCheck/-properties "ToolShed.Data.List".+-}++module ToolShed.Test.Data.List(+-- * Functions+ quickChecks+) where++import qualified Data.List+import qualified Data.Ord+import qualified Data.Set+import qualified ToolShed.Data.List+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++-- | Defines invariant properties.+quickChecks :: IO ()+quickChecks =+ Test.QuickCheck.quickCheck prop_chunk+ >> Test.QuickCheck.quickCheck `mapM_` [prop_findConvergence, prop_gatherSet]+ >> Test.QuickCheck.quickCheck prop_gatherBy+ >> Test.QuickCheck.quickCheck prop_permutations+ >> Test.QuickCheck.quickCheck prop_linearise+ >> Test.QuickCheck.quickCheck prop_merge+ where+ prop_chunk :: Int -> [Int] -> Test.QuickCheck.Property+ prop_chunk i l = Test.QuickCheck.label "prop_chunk" $ concat (ToolShed.Data.List.chunk (succ $ abs i) l) == l++ prop_findConvergence, prop_gatherSet :: Int -> Test.QuickCheck.Property+ prop_findConvergence = Test.QuickCheck.label "prop_findConvergence" . (== 0) . ToolShed.Data.List.findConvergence . iterate (fst . (`quotRem` 2))+ prop_gatherSet n = Test.QuickCheck.label "prop_gatherSet" . (== l) . concat . ToolShed.Data.List.gather $ Data.Set.fromDistinctAscList l where+ l = [0 .. n `mod` 1024]++ prop_gatherBy :: [Int] -> Test.QuickCheck.Property+ prop_gatherBy l = Test.QuickCheck.label "prop_gatherBy" $ map Data.List.sort (+ ToolShed.Data.List.gatherBy even l+ ) == (+ map Data.List.sort . Data.List.groupBy (ToolShed.Data.List.equalityBy even) $ Data.List.sortBy (Data.Ord.comparing even) l+ )++ prop_permutations :: [[Int]] -> Test.QuickCheck.Property+ prop_permutations l = not (null l') ==> Test.QuickCheck.label "prop_permutations" $ length (ToolShed.Data.List.permutations l') == product (map length l') where+ l' = take 6 $ map (take 6) l --Limit the task.++ prop_linearise :: [(Int, Int)] -> Test.QuickCheck.Property+ prop_linearise l = Test.QuickCheck.label "prop_linearise" $ map (\[x, y] -> (x, y)) (ToolShed.Data.List.chunk 2 $ ToolShed.Data.List.linearise l) == l++ prop_merge :: [Int] -> [Int] -> Test.QuickCheck.Property+ prop_merge x y = Test.QuickCheck.label "prop_merge" $ ToolShed.Data.List.merge (Data.List.sort x) (Data.List.sort y) == Data.List.sort (x ++ y)+
+ src/ToolShed/Test/Data/List/Runlength.hs view
@@ -0,0 +1,36 @@+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Defines /QuickCheck/-properties "ToolShed.Data.List.Runlength".+-}++module ToolShed.Test.Data.List.Runlength(+-- * Functions+ quickChecks+) where++import qualified ToolShed.Data.List.Runlength+import qualified Test.QuickCheck++-- | Defines invariant properties.+quickChecks :: IO ()+quickChecks = Test.QuickCheck.quickCheck prop_runLength where+ prop_runLength :: [Int] -> Test.QuickCheck.Property+ prop_runLength l = Test.QuickCheck.label "prop_runLength" $ ToolShed.Data.List.Runlength.decode (ToolShed.Data.List.Runlength.encode l) == l+
+ src/ToolShed/Test/Data/List/Splits.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE CPP #-}+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Defines /QuickCheck/-properties "ToolShed.Data.List.Splits".+-}++module ToolShed.Test.Data.List.Splits(+-- * Functions+ quickChecks+) where++import Control.Arrow((***))+import qualified Data.List+import qualified Data.Ord+import qualified Data.Set+import qualified ToolShed.Data.List.Splits+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++#if MIN_VERSION_base(4,3,0)+import Data.Tuple(swap)+#else+-- | Swap the components of a pair.+swap :: (a, b) -> (b, a)+swap (a, b) = (b, a)+#endif++-- | Defines invariant properties.+quickChecks :: IO ()+quickChecks = Test.QuickCheck.quickCheck prop_splitsFrom where+ prop_splitsFrom :: Int -> [Int] -> Test.QuickCheck.Property+ prop_splitsFrom i l = not (null l) ==> Test.QuickCheck.label "prop_splitsFrom" $ take n (ToolShed.Data.List.Splits.splitsLeftFrom index l) == take n (map (swap . (reverse *** reverse)) . ToolShed.Data.List.Splits.splitsRightFrom (length l - index) $ reverse l) where+ index = i `mod` length l+ n = succ index
+ src/ToolShed/Test/QuickChecks.hs view
@@ -0,0 +1,37 @@+{-+ Copyright (C) 2012 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Calls the /quickChecks/-functions for modules supporting this feature.+-}++module ToolShed.Test.QuickChecks(+-- * Functions+ run+) where++import qualified ToolShed.Test.Data.List+import qualified ToolShed.Test.Data.List.Runlength+import qualified ToolShed.Test.Data.List.Splits++-- | Run the /quickChecks/-functions for modules supporting this feature.+run :: IO ()+run = putStrLn "Data.List" >> ToolShed.Test.Data.List.quickChecks+ >> putStrLn "Data.List.Runlength" >> ToolShed.Test.Data.List.Runlength.quickChecks+ >> putStrLn "Data.List.Splits" >> ToolShed.Test.Data.List.Splits.quickChecks+
− src/ToolShed/TimeAction.hs
@@ -1,49 +0,0 @@-{-- Copyright (C) 2011 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Determines the CPU-time, required to evaluate the specified IO-action.---}--module ToolShed.TimeAction(--- * Functions- getCPUSeconds,- printCPUSeconds-) where--import qualified System.CPUTime-import qualified System.IO---- | Time the specified IO-action, returning the seconds and result as a 'Pair'.-getCPUSeconds :: Fractional seconds => IO result -> IO (seconds, result)-getCPUSeconds action = do- startTime <- System.CPUTime.getCPUTime- result <- action- endTime <- System.CPUTime.getCPUTime-- return (fromInteger (endTime - startTime) / 1e12 {-convert from pico-seconds-}, result)---- | Print the time required by the specified IO-action.-printCPUSeconds :: IO result -> IO result-printCPUSeconds action = do- (cpuSeconds, result) <- getCPUSeconds action-- System.IO.hPutStrLn System.IO.stderr $ "CPU-seconds:\t" ++ show (cpuSeconds :: Double)-- return {-to IO-monad-} result
− src/ToolShed/TimePure.hs
@@ -1,78 +0,0 @@-{-# LANGUAGE CPP #-}-{-- Copyright (C) 2010 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Determines the CPU-time, required to evaluate the specified pure expression.---}--module ToolShed.TimePure (--- * Functions- getCPUSeconds,- printCPUSeconds-) where--import qualified System.CPUTime-import qualified System.IO--{-- The function used to force evaluation of the pure expression 'rnf',- and the type-class which the polymorphic data must implement 'NFData',- has been relocated from module "Control.Parallel.Strategies" to "Control.DeepSeq".--}-#ifdef HAVE_DEEPSEQ-import Control.DeepSeq(NFData, deepseq)-#else-import Control.Parallel(pseq)-import Control.Parallel.Strategies(NFData, rnf)-#endif--{- |- * Time the specified pure expression, returning the seconds and result as a 'Pair'.-- * CAVEAT: as a side-effect, the expression is /deep/ evaluated.--}-getCPUSeconds :: (Fractional seconds, NFData expression)- => expression -- ^ Arbitrary polymorphic expression.- -> IO (seconds, expression) -- ^ The original expression, tagged with the CPU-seconds taken.-getCPUSeconds expression = do- start <- System.CPUTime.getCPUTime- end <--#ifdef HAVE_DEEPSEQ- expression `deepseq`-#else- rnf expression `pseq`-#endif- System.CPUTime.getCPUTime-- return {-to IO-monad-} (fromInteger (end - start) / 1e12 {-convert from pico-seconds-}, expression)--{- |- * Print the time required by the specified pure expression.-- * CAVEAT: as a side-effect, the expression is /deep/ evaluated.--}-printCPUSeconds :: NFData expression => expression -> IO expression-printCPUSeconds expression = do- (cpuSeconds, result) <- getCPUSeconds expression-- System.IO.hPutStrLn System.IO.stderr $ "CPU-seconds:\t" ++ show (cpuSeconds :: Double)-- return {-to IO-monad-} result-
− src/ToolShed/Unsafe.hs
@@ -1,56 +0,0 @@-{-- Copyright (C) 2010 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@]-- * Provides impure functions to print & return the specified value.-- * The pure interfaces permit calls from outside the /IO-monad/.-- [@TODO@] Review function-names.--}--module ToolShed.Unsafe(--- * Functions- print',- printShow-) where--import qualified System.IO-import qualified System.IO.Unsafe--{-# NOINLINE print' #-}--- | A transparent print-function, for use in debugging.-print' :: Show s- => s -- ^ Arbitrary polymorphic input-data.- -> s -- ^ Output (identical to input).-print' = System.IO.Unsafe.unsafePerformIO . f where- f :: Show s => s -> IO s- f x = System.IO.hPrint System.IO.stderr x >> return {-to IO-monad-} x--{-# NOINLINE printShow #-}--- | A transparent print-function, which prepends the specified label, for use in debugging.-printShow :: Show s- => String -- ^ The label to be printed before the value of the input data.- -> s -- ^ Arbitrary polymorphic input-data.- -> s -- ^ Output (identical to input).-printShow label = System.IO.Unsafe.unsafePerformIO . f label where- f :: Show s => String -> s -> IO s- f s x = System.IO.hPutStrLn System.IO.stderr (s ++ show x) >> return {-to IO-monad-} x-
toolshed.cabal view
@@ -1,6 +1,6 @@ --Package-properties Name: toolshed-Version: 0.12.0.0+Version: 0.13.0.0 Cabal-Version: >= 1.6 Copyright: (C) 2010 Dr. Alistair Ward License: GPL@@ -30,24 +30,56 @@ hs-source-dirs: src Exposed-modules:+ ToolShed.Arbitrary.Array+ ToolShed.Arbitrary.ArrayElem+ ToolShed.Arbitrary.Map+ ToolShed.Arbitrary.Set+ ToolShed.Data.List+ ToolShed.Data.List.Runlength+ ToolShed.Data.List.Splits+ ToolShed.Data.Pair ToolShed.Defaultable- ToolShed.ListPlus ToolShed.Options- ToolShed.Pair ToolShed.SelfValidate- ToolShed.TimeAction- ToolShed.TimePure- ToolShed.Unsafe + ToolShed.System.File+ ToolShed.System.Random+ ToolShed.System.TimeAction+ ToolShed.System.TimePure - Build-depends: base == 4.*+ Build-depends:+ array,+ base == 4.*,+ containers,+ directory,+ filepath,+ QuickCheck >= 2.2,+ random GHC-options: -Wall -O2- GHC-prof-options: -prof -auto-all -caf-all + if impl(ghc >= 7.4.1)+ GHC-prof-options: -prof -fprof-auto -fprof-cafs+ else+ GHC-prof-options: -prof -auto-all -caf-all+ if flag(haveDeepSeq) Build-depends: deepseq >= 1.1 CPP-options: -DHAVE_DEEPSEQ if impl(ghc >= 7.0) && flag(llvm) GHC-options: -fllvm++Executable toolshed+ hs-source-dirs: src++ Main-Is: Main.hs++ Other-modules:+ ToolShed.Test.QuickChecks+ ToolShed.Test.Data.List+ ToolShed.Test.Data.List.Runlength+ ToolShed.Test.Data.List.Splits++ Build-depends:+ Cabal >= 1.6 && < 2