From 576d062f010fe1ccfef285e072e6ebb0a99147f8 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Tue, 20 Mar 2018 17:16:40 -0400 Subject: [PATCH 01/29] initial implementation of a more comprehensive test suite --- Data/Primitive/Array.hs | 12 ++++++++ test/main.hs | 56 ++++++++++++++++++++++++++++++++++---- test/primitive-tests.cabal | 4 +++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index cfea04d2..86096907 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -59,6 +59,8 @@ import Data.Semigroup import Text.ParserCombinators.ReadP +import Data.Functor.Classes (Eq1(..),Show1(..)) + -- | Boxed arrays data Array a = Array { array# :: Array# a @@ -341,6 +343,11 @@ instance Eq a => Eq (Array a) where where loop i | i < 0 = True | otherwise = indexArray a1 i == indexArray a2 i && loop (i-1) +instance Eq1 Array where + liftEq p a1 a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - 1) + where loop i | i < 0 = True + | otherwise = p (indexArray a1 i) (indexArray a2 i) && loop (i-1) + instance Eq (MutableArray s a) where ma1 == ma2 = isTrue# (sameMutableArray# (marray# ma1) (marray# ma2)) @@ -592,6 +599,11 @@ instance Show a => Show (Array a) where showString "fromListN " . shows (sizeofArray a) . showString " " . shows (toList a) +instance Show1 Array where + liftShowsPrec elemShowsPrec elemListShowsPrec p a = showParen (p > 10) $ + showString "fromListN " . shows (sizeofArray a) . showString " " + . liftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList a) + instance Read a => Read (Array a) where readsPrec p = readParen (p > 10) . readP_to_S $ do () <$ string "fromListN" diff --git a/test/main.hs b/test/main.hs index 433d2553..15d0a8d8 100644 --- a/test/main.hs +++ b/test/main.hs @@ -1,4 +1,8 @@ -{-# LANGUAGE CPP, MagicHash, UnboxedTuples #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} import Control.Monad import Control.Monad.Primitive @@ -9,6 +13,7 @@ import Data.Primitive.Array import Data.Primitive.ByteArray import Data.Primitive.Types import Data.Word +import Data.Proxy (Proxy(..)) import GHC.Int import GHC.IO import GHC.Prim @@ -16,14 +21,33 @@ import GHC.Prim import Data.Semigroup (stimes) #endif --- Since we only have two test cases right now, I'm going to avoid the --- issue of choosing a test framework for the moment. This also keeps the --- package as a whole light on dependencies. +import Test.Tasty (defaultMain,testGroup,TestTree) +import Test.QuickCheck (Arbitrary,Arbitrary1,Gen) +import qualified Test.Tasty.QuickCheck as TQC +import qualified Test.QuickCheck as QC +import qualified Test.QuickCheck.Classes as QCC +import qualified Data.List as L main :: IO () main = do - testArray - testByteArray + testArray + testByteArray + defaultMain $ testGroup "Array" + [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 Array)) + ] + +-- on GHC 7.4, Proxy is not polykinded, so we need this instead. +data Proxy1 (f :: * -> *) = Proxy1 + +lawsToTest :: QCC.Laws -> TestTree +lawsToTest (QCC.Laws name pairs) = testGroup name (map (uncurry TQC.testProperty) pairs) testArray :: IO () testArray = do @@ -69,3 +93,23 @@ mkByteArray xs = runST $ do marr <- newByteArray (length xs * sizeOf (head xs)) sequence $ zipWith (writeByteArray marr) [0..] xs unsafeFreezeByteArray marr + +instance Arbitrary1 Array where + liftArbitrary elemGen = fmap fromList (QC.liftArbitrary elemGen) + +instance Arbitrary a => Arbitrary (Array a) where + arbitrary = fmap fromList QC.arbitrary + +instance Arbitrary ByteArray where + arbitrary = do + xs <- QC.arbitrary :: Gen [Word8] + return $ runST $ do + a <- newByteArray (L.length xs) + iforM_ xs $ \ix x -> do + writeByteArray a ix x + unsafeFreezeByteArray a + +iforM_ :: Monad m => [a] -> (Int -> a -> m b) -> m () +iforM_ xs0 f = go 0 xs0 where + go !_ [] = return () + go !ix (x : xs) = f ix x >> go (ix + 1) xs diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index d2d00ca4..abbe0dea 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -31,6 +31,10 @@ test-suite test build-depends: base >= 4.5 && < 4.12 , ghc-prim , primitive + , QuickCheck + , tasty + , tasty-quickcheck + , quickcheck-classes >= 0.4 ghc-options: -O2 source-repository head From b18e93a64866b55b20bd02632eee76bac2266724 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 07:21:16 -0400 Subject: [PATCH 02/29] fix laziness of Eq1 instance. Add Ord1 instance. --- Data/Primitive/Array.hs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 00b6e9c5..56e09880 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -63,7 +63,7 @@ import Data.Functor.Identity import Text.ParserCombinators.ReadP -import Data.Functor.Classes (Eq1(..),Show1(..)) +import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..)) -- | Boxed arrays data Array a = Array @@ -300,7 +300,9 @@ instance Eq a => Eq (Array a) where instance Eq1 Array where liftEq p a1 a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - 1) where loop i | i < 0 = True - | otherwise = p (indexArray a1 i) (indexArray a2 i) && loop (i-1) + | (# x1 #) <- indexArray## a1 i + , (# x2 #) <- indexArray## a2 i + , otherwise = p x1 x2 && loop (i-1) instance Eq (MutableArray s a) where ma1 == ma2 = isTrue# (sameMutableArray# (marray# ma1) (marray# ma2)) @@ -316,6 +318,17 @@ instance Ord a => Ord (Array a) where = compare x1 x2 `mappend` loop (i+1) | otherwise = compare (sizeofArray a1) (sizeofArray a2) +instance Ord1 Array where + liftCompare elemCompare a1 a2 = loop 0 + where + mn = sizeofArray a1 `min` sizeofArray a2 + loop i + | i < mn + , (# x1 #) <- indexArray## a1 i + , (# x2 #) <- indexArray## a2 i + = elemCompare x1 x2 `mappend` loop (i+1) + | otherwise = compare (sizeofArray a1) (sizeofArray a2) + instance Foldable Array where -- Note: we perform the array lookups eagerly so we won't -- create thunks to perform lookups even if GHC can't see From be53e0082996e98c0293bd99e19cb5d94d69c13e Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 07:44:27 -0400 Subject: [PATCH 03/29] correct the definition of <*> --- Data/Primitive/Array.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 56e09880..5912f6a2 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -546,6 +546,7 @@ instance Applicative Array where do x <- indexArrayM a j writeArray mb (off + j) (f x) + go2 off f (j + 1) go1 0 unsafeFreezeArray mb where szab = sizeofArray ab ; sza = sizeofArray a From 190188b8288b7adc045e897c20958bc5850d07bf Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 11:17:26 -0400 Subject: [PATCH 04/29] Improve compatibility with older versions of GHC and transformers --- Data/Primitive/Array.hs | 79 ++++++++++++++++++++++---------------- test/primitive-tests.cabal | 2 +- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 5912f6a2..641c24f1 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -290,44 +290,46 @@ createArray n x f = runST $ do die :: String -> String -> a die fun problem = error $ "Data.Primitive.Array." ++ fun ++ ": " ++ problem +arrayLiftEq :: (a -> b -> Bool) -> Array a -> Array b -> Bool +arrayLiftEq p a1 a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - 1) + where loop i | i < 0 = True + | (# x1 #) <- indexArray## a1 i + , (# x2 #) <- indexArray## a2 i + , otherwise = p x1 x2 && loop (i-1) + instance Eq a => Eq (Array a) where - a1 == a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - 1) - where loop i | i < 0 = True - | (# x1 #) <- indexArray## a1 i - , (# x2 #) <- indexArray## a2 i - = x1 == x2 && loop (i-1) + a1 == a2 = arrayLiftEq (==) a1 a2 instance Eq1 Array where - liftEq p a1 a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - 1) - where loop i | i < 0 = True - | (# x1 #) <- indexArray## a1 i - , (# x2 #) <- indexArray## a2 i - , otherwise = p x1 x2 && loop (i-1) +#if MIN_VERSION_base(4,9,0) + liftEq = arrayLiftEq +#else + eq1 = arrayLiftEq (==) +#endif instance Eq (MutableArray s a) where ma1 == ma2 = isTrue# (sameMutableArray# (marray# ma1) (marray# ma2)) +arrayLiftCompare :: (a -> b -> Ordering) -> Array a -> Array b -> Ordering +arrayLiftCompare elemCompare a1 a2 = loop 0 + where + mn = sizeofArray a1 `min` sizeofArray a2 + loop i + | i < mn + , (# x1 #) <- indexArray## a1 i + , (# x2 #) <- indexArray## a2 i + = elemCompare x1 x2 `mappend` loop (i+1) + | otherwise = compare (sizeofArray a1) (sizeofArray a2) + instance Ord a => Ord (Array a) where - compare a1 a2 = loop 0 - where - mn = sizeofArray a1 `min` sizeofArray a2 - loop i - | i < mn - , (# x1 #) <- indexArray## a1 i - , (# x2 #) <- indexArray## a2 i - = compare x1 x2 `mappend` loop (i+1) - | otherwise = compare (sizeofArray a1) (sizeofArray a2) + compare a1 a2 = arrayLiftCompare compare a1 a2 instance Ord1 Array where - liftCompare elemCompare a1 a2 = loop 0 - where - mn = sizeofArray a1 `min` sizeofArray a2 - loop i - | i < mn - , (# x1 #) <- indexArray## a1 i - , (# x2 #) <- indexArray## a2 i - = elemCompare x1 x2 `mappend` loop (i+1) - | otherwise = compare (sizeofArray a1) (sizeofArray a2) +#if MIN_VERSION_base(4,9,0) + liftCompare = arrayLiftCompare +#else + compare1 = arrayLiftCompare compare +#endif instance Foldable Array where -- Note: we perform the array lookups eagerly so we won't @@ -670,15 +672,24 @@ instance Monoid (Array a) where in go 0 l where sz = sum . fmap sizeofArray $ l +arrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> Array a -> ShowS +arrayLiftShowsPrec elemShowsPrec elemListShowsPrec p a = showParen (p > 10) $ + showString "fromListN " . shows (sizeofArray a) . showString " " + . listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList a) + +-- this need to be included for older ghcs +listLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> [a] -> ShowS +listLiftShowsPrec _ sl _ = sl + instance Show a => Show (Array a) where - showsPrec p a = showParen (p > 10) $ - showString "fromListN " . shows (sizeofArray a) . showString " " - . shows (toList a) + showsPrec p a = arrayLiftShowsPrec showsPrec showList p a instance Show1 Array where - liftShowsPrec elemShowsPrec elemListShowsPrec p a = showParen (p > 10) $ - showString "fromListN " . shows (sizeofArray a) . showString " " - . liftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList a) +#if MIN_VERSION_base(4,9,0) + liftShowsPrec = arrayLiftShowsPrec +#else + showsPrec1 = arrayLiftShowsPrec showsPrec showList +#endif instance Read a => Read (Array a) where readsPrec p = readParen (p > 10) . readP_to_S $ do diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index abbe0dea..c81f33a7 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -34,7 +34,7 @@ test-suite test , QuickCheck , tasty , tasty-quickcheck - , quickcheck-classes >= 0.4 + , quickcheck-classes == 0.4.1 ghc-options: -O2 source-repository head From c7d3c77dab64595534a77962f38a807da61466a7 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 11:36:23 -0400 Subject: [PATCH 05/29] add tests for SmallArray and ByteArray. add Eq1, Ord1, and Show1 for SmallArray --- Data/Primitive/SmallArray.hs | 80 ++++++++++++++++++++++++++---------- test/main.hs | 43 +++++++++++++++---- 2 files changed, 93 insertions(+), 30 deletions(-) diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index a4ee966b..a02896fc 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -91,6 +91,8 @@ import Data.Primitive.Array import Data.Traversable #endif +import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..)) + #if HAVE_SMALL_ARRAY data SmallArray a = SmallArray (SmallArray# a) deriving Typeable @@ -111,6 +113,9 @@ newtype SmallArray a = SmallArray (Array a) deriving , MonadFix , Monoid , Typeable + , Eq1 + , Ord1 + , Show1 ) #if MIN_VERSION_base(4,7,0) @@ -443,31 +448,50 @@ infixl 1 ? noOp :: a -> ST s () noOp = const $ pure () +smallArrayLiftEq :: (a -> b -> Bool) -> SmallArray a -> SmallArray b -> Bool +smallArrayLiftEq p sa1 sa2 = length sa1 == length sa2 && loop (length sa1 - 1) + where + loop i + | i < 0 + = True + | (# x #) <- indexSmallArray## sa1 i + , (# y #) <- indexSmallArray## sa2 i + = p x y && loop (i-1) + +instance Eq1 SmallArray where +#if MIN_VERSION_base(4,9,0) + liftEq = smallArrayLiftEq +#else + eq1 = smallArrayLiftEq (==) +#endif + instance Eq a => Eq (SmallArray a) where - sa1 == sa2 = length sa1 == length sa2 && loop (length sa1 - 1) - where - loop i - | i < 0 - = True - | (# x #) <- indexSmallArray## sa1 i - , (# y #) <- indexSmallArray## sa2 i - = x == y && loop (i-1) + sa1 == sa2 = smallArrayLiftEq (==) sa1 sa2 instance Eq (SmallMutableArray s a) where SmallMutableArray sma1# == SmallMutableArray sma2# = isTrue# (sameSmallMutableArray# sma1# sma2#) -instance Ord a => Ord (SmallArray a) where - compare a1 a2 = loop 0 - where - mn = length a1 `min` length a2 - loop i - | i < mn - , (# x1 #) <- indexSmallArray## a1 i - , (# x2 #) <- indexSmallArray## a2 i - = compare x1 x2 `mappend` loop (i+1) - | otherwise = compare (length a1) (length a2) +smallArrayLiftCompare :: (a -> b -> Ordering) -> SmallArray a -> SmallArray b -> Ordering +smallArrayLiftCompare elemCompare a1 a2 = loop 0 + where + mn = length a1 `min` length a2 + loop i + | i < mn + , (# x1 #) <- indexSmallArray## a1 i + , (# x2 #) <- indexSmallArray## a2 i + = elemCompare x1 x2 `mappend` loop (i+1) + | otherwise = compare (length a1) (length a2) + +instance Ord1 SmallArray where +#if MIN_VERSION_base(4,9,0) + liftCompare = smallArrayLiftCompare +#else + compare1 = smallArrayLiftCompare compare +#endif +instance Ord a => Ord (SmallArray a) where + compare sa1 sa2 = smallArrayLiftCompare compare sa1 sa2 instance Foldable SmallArray where -- Note: we perform the array lookups eagerly so we won't @@ -765,10 +789,24 @@ instance IsList (SmallArray a) where fromList l = fromListN (length l) l toList = Foldable.toList +smallArrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> SmallArray a -> ShowS +smallArrayLiftShowsPrec elemShowsPrec elemListShowsPrec p sa = showParen (p > 10) $ + showString "fromListN " . shows (length sa) . showString " " + . listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList sa) + +-- this need to be included for older ghcs +listLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> [a] -> ShowS +listLiftShowsPrec _ sl _ = sl + instance Show a => Show (SmallArray a) where - showsPrec p sa = showParen (p > 10) $ - showString "fromListN " . shows (length sa) . showString " " - . shows (toList sa) + showsPrec p sa = smallArrayLiftShowsPrec showsPrec showList p sa + +instance Show1 SmallArray where +#if MIN_VERSION_base(4,9,0) + liftShowsPrec = smallArrayLiftShowsPrec +#else + showsPrec1 = smallArrayLiftShowsPrec showsPrec showList +#endif instance Read a => Read (SmallArray a) where readPrec = parens . prec 10 $ do diff --git a/test/main.hs b/test/main.hs index 15d0a8d8..789b3dd9 100644 --- a/test/main.hs +++ b/test/main.hs @@ -12,6 +12,7 @@ import Data.Primitive import Data.Primitive.Array import Data.Primitive.ByteArray import Data.Primitive.Types +import Data.Primitive.SmallArray import Data.Word import Data.Proxy (Proxy(..)) import GHC.Int @@ -32,15 +33,32 @@ main :: IO () main = do testArray testByteArray - defaultMain $ testGroup "Array" - [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int))) - , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int))) - , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int))) - , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int))) - , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 Array)) - , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array)) - , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array)) - , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 Array)) + defaultMain $ testGroup "properties" + [ testGroup "Array" + [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 Array)) + ] + , testGroup "SmallArray" + [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int))) + , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int))) + , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int))) + , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int))) + , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 SmallArray)) + , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 SmallArray)) + , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 SmallArray)) + , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 SmallArray)) + ] + , testGroup "ByteArray" + [ lawsToTest (QCC.eqLaws (Proxy :: Proxy ByteArray)) + , lawsToTest (QCC.ordLaws (Proxy :: Proxy ByteArray)) + , lawsToTest (QCC.isListLaws (Proxy :: Proxy ByteArray)) + ] ] -- on GHC 7.4, Proxy is not polykinded, so we need this instead. @@ -100,6 +118,12 @@ instance Arbitrary1 Array where instance Arbitrary a => Arbitrary (Array a) where arbitrary = fmap fromList QC.arbitrary +instance Arbitrary1 SmallArray where + liftArbitrary elemGen = fmap fromList (QC.liftArbitrary elemGen) + +instance Arbitrary a => Arbitrary (SmallArray a) where + arbitrary = fmap fromList QC.arbitrary + instance Arbitrary ByteArray where arbitrary = do xs <- QC.arbitrary :: Gen [Word8] @@ -109,6 +133,7 @@ instance Arbitrary ByteArray where writeByteArray a ix x unsafeFreezeByteArray a + iforM_ :: Monad m => [a] -> (Int -> a -> m b) -> m () iforM_ xs0 f = go 0 xs0 where go !_ [] = return () From d1c3dfba9e5fc15d43ca952af6f2c677ac4c60fc Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 11:40:22 -0400 Subject: [PATCH 06/29] fix implementation of <*> for SmallArray. The previous implementation did a pretty cool trick with a fixed-point combinator, but it didn't work. I couldn't figure out how to make it work, so I just copied the implementation used for Array and adapted it to SmallArray. --- Data/Primitive/SmallArray.hs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index a02896fc..6957f66c 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -656,17 +656,21 @@ instance Applicative SmallArray where in go 0 where sza = sizeofSmallArray a ; szb = sizeofSmallArray b - sf <*> sx = createSmallArray (lf*lx) (die "<*>" "impossible") $ \smb -> - fix ? 0 $ \outer i -> when (i < lf) $ do - f <- indexSmallArrayM sf i - fix ? 0 $ \inner j -> - when (j < lx) $ do - x <- indexSmallArrayM sx j - writeSmallArray smb (lf*i + j) (f x) - *> inner (j+1) - outer $ i+1 - where - lf = length sf ; lx = length sx + ab <*> a = runST $ do + mb <- newSmallArray (szab*sza) $ die "<*>" "impossible" + let go1 i = when (i < szab) $ + do + f <- indexSmallArrayM ab i + go2 (i*sza) f 0 + go1 (i+1) + go2 off f j = when (j < sza) $ + do + x <- indexSmallArrayM a j + writeSmallArray mb (off + j) (f x) + go2 off f (j + 1) + go1 0 + unsafeFreezeSmallArray mb + where szab = sizeofSmallArray ab ; sza = sizeofSmallArray a instance Alternative SmallArray where empty = emptySmallArray From 2f13dea9a9095b2f2e9b6540c0fa36987345c2f7 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 11:54:33 -0400 Subject: [PATCH 07/29] correct foldrByteArray, which fixes the IsList implementation for ByteArray. Also, improve the performance of ByteArray's fromListN function --- Data/Primitive/ByteArray.hs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Data/Primitive/ByteArray.hs b/Data/Primitive/ByteArray.hs index a9576bce..2e4e69c6 100644 --- a/Data/Primitive/ByteArray.hs +++ b/Data/Primitive/ByteArray.hs @@ -44,7 +44,6 @@ module Data.Primitive.ByteArray ( import Control.Monad.Primitive import Control.Monad.ST -import Control.Monad ( zipWithM_ ) import Data.Primitive.Types import Foreign.C.Types @@ -181,12 +180,16 @@ foldrByteArray f z arr = go 0 go i | sizeofByteArray arr > i * sz = f (indexByteArray arr i) (go (i+1)) | otherwise = z - sz = sizeofByteArray arr + sz = sizeOf (undefined :: a) fromListN :: Prim a => Int -> [a] -> ByteArray -fromListN n xs = runST $ do - marr <- newByteArray (n * sizeOf (head xs)) - zipWithM_ (writeByteArray marr) [0..n] xs +fromListN n ys = runST $ do + marr <- newByteArray (n * sizeOf (head ys)) + let go !_ [] = return () + go !ix (x : xs) = do + writeByteArray marr ix x + go (ix + 1) xs + go 0 ys unsafeFreezeByteArray marr #if __GLASGOW_HASKELL__ >= 702 From 19c67f7a4c76f8d01fc72deac3227c43406b1f81 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 12:12:39 -0400 Subject: [PATCH 08/29] improve compatibility with older transformers and base --- Data/Primitive/Array.hs | 6 +++--- Data/Primitive/SmallArray.hs | 6 +++--- primitive.cabal | 2 +- test/main.hs | 4 ++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 641c24f1..896f64ba 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -301,7 +301,7 @@ instance Eq a => Eq (Array a) where a1 == a2 = arrayLiftEq (==) a1 a2 instance Eq1 Array where -#if MIN_VERSION_base(4,9,0) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftEq = arrayLiftEq #else eq1 = arrayLiftEq (==) @@ -325,7 +325,7 @@ instance Ord a => Ord (Array a) where compare a1 a2 = arrayLiftCompare compare a1 a2 instance Ord1 Array where -#if MIN_VERSION_base(4,9,0) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftCompare = arrayLiftCompare #else compare1 = arrayLiftCompare compare @@ -685,7 +685,7 @@ instance Show a => Show (Array a) where showsPrec p a = arrayLiftShowsPrec showsPrec showList p a instance Show1 Array where -#if MIN_VERSION_base(4,9,0) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftShowsPrec = arrayLiftShowsPrec #else showsPrec1 = arrayLiftShowsPrec showsPrec showList diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index 6957f66c..c690e768 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -459,7 +459,7 @@ smallArrayLiftEq p sa1 sa2 = length sa1 == length sa2 && loop (length sa1 - 1) = p x y && loop (i-1) instance Eq1 SmallArray where -#if MIN_VERSION_base(4,9,0) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftEq = smallArrayLiftEq #else eq1 = smallArrayLiftEq (==) @@ -484,7 +484,7 @@ smallArrayLiftCompare elemCompare a1 a2 = loop 0 | otherwise = compare (length a1) (length a2) instance Ord1 SmallArray where -#if MIN_VERSION_base(4,9,0) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftCompare = smallArrayLiftCompare #else compare1 = smallArrayLiftCompare compare @@ -806,7 +806,7 @@ instance Show a => Show (SmallArray a) where showsPrec p sa = smallArrayLiftShowsPrec showsPrec showList p sa instance Show1 SmallArray where -#if MIN_VERSION_base(4,9,0) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftShowsPrec = smallArrayLiftShowsPrec #else showsPrec1 = smallArrayLiftShowsPrec showsPrec showList diff --git a/primitive.cabal b/primitive.cabal index 5cd46386..35e0b39c 100644 --- a/primitive.cabal +++ b/primitive.cabal @@ -52,7 +52,7 @@ Library Build-Depends: base >= 4.5 && < 4.12 , ghc-prim >= 0.2 && < 0.6 - , transformers >= 0.2 && < 0.6 + , transformers >= 0.4 && < 0.6 Ghc-Options: -O2 -Wall diff --git a/test/main.hs b/test/main.hs index 789b3dd9..3944da36 100644 --- a/test/main.hs +++ b/test/main.hs @@ -38,7 +38,9 @@ main = do [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int))) , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int))) , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int))) +#if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int))) +#endif , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array)) @@ -48,7 +50,9 @@ main = do [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int))) , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int))) , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int))) +#if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int))) +#endif , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 SmallArray)) From 6f39a7cde7e9b2f88896a442f5c3e61406bc08fc Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 12:33:57 -0400 Subject: [PATCH 09/29] add tagged as dependency of test suite so Data.Proxy is available for older GHCs --- test/primitive-tests.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index c81f33a7..c4312d58 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -34,6 +34,7 @@ test-suite test , QuickCheck , tasty , tasty-quickcheck + , tagged , quickcheck-classes == 0.4.1 ghc-options: -O2 From d2ae3844e7168909a030776df794501d20c89f1c Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 13:15:02 -0400 Subject: [PATCH 10/29] use spaces instead of tab in test cabal file --- test/primitive-tests.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index c4312d58..866df731 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -34,7 +34,7 @@ test-suite test , QuickCheck , tasty , tasty-quickcheck - , tagged + , tagged , quickcheck-classes == 0.4.1 ghc-options: -O2 From a5bf17aa39943720b9839d7a75ff077a70d39121 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 13:31:07 -0400 Subject: [PATCH 11/29] guard isListLaws with CPP everywhere --- test/main.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/main.hs b/test/main.hs index 3944da36..2aaf9220 100644 --- a/test/main.hs +++ b/test/main.hs @@ -61,7 +61,9 @@ main = do , testGroup "ByteArray" [ lawsToTest (QCC.eqLaws (Proxy :: Proxy ByteArray)) , lawsToTest (QCC.ordLaws (Proxy :: Proxy ByteArray)) +#if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy ByteArray)) +#endif ] ] From bd5e8e1e9809823b146642c0910f7be5522da979 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 15:15:35 -0400 Subject: [PATCH 12/29] redefine fromList function for small array in test suite --- Data/Primitive/SmallArray.hs | 2 ++ test/main.hs | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index c690e768..a489122f 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -54,6 +54,8 @@ module Data.Primitive.SmallArray , unsafeThawSmallArray , sizeofSmallArray , sizeofSmallMutableArray + , fromListN + , fromList , unsafeTraverseSmallArray ) where diff --git a/test/main.hs b/test/main.hs index 2aaf9220..c8ea4984 100644 --- a/test/main.hs +++ b/test/main.hs @@ -4,7 +4,9 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE UnboxedTuples #-} +import Control.Applicative import Control.Monad +import Control.Monad.Fix (fix) import Control.Monad.Primitive import Control.Monad.ST import Data.Monoid @@ -125,10 +127,10 @@ instance Arbitrary a => Arbitrary (Array a) where arbitrary = fmap fromList QC.arbitrary instance Arbitrary1 SmallArray where - liftArbitrary elemGen = fmap fromList (QC.liftArbitrary elemGen) + liftArbitrary elemGen = fmap smallArrayFromList (QC.liftArbitrary elemGen) instance Arbitrary a => Arbitrary (SmallArray a) where - arbitrary = fmap fromList QC.arbitrary + arbitrary = fmap smallArrayFromList QC.arbitrary instance Arbitrary ByteArray where arbitrary = do @@ -144,3 +146,19 @@ iforM_ :: Monad m => [a] -> (Int -> a -> m b) -> m () iforM_ xs0 f = go 0 xs0 where go !_ [] = return () go !ix (x : xs) = f ix x >> go (ix + 1) xs + +infixl 1 ? +(?) :: (a -> b -> c) -> (b -> a -> c) +(?) = flip +{-# INLINE (?) #-} + +smallArrayFromListN :: Int -> [a] -> SmallArray a +smallArrayFromListN n l = runST $ do + sma <- newSmallArray n (error "primitive:test, smallArrayFromListN, mismatched size and list") + fix ? 0 ? l $ \go i li -> case li of + [] -> pure () + x:xs -> writeSmallArray sma i x *> go (i+1) xs + unsafeFreezeSmallArray sma + +smallArrayFromList :: [a] -> SmallArray a +smallArrayFromList l = smallArrayFromListN (length l) l From 144534589fdf6f9cca9f3687608e7eceaa605698 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 15:19:46 -0400 Subject: [PATCH 13/29] remove the accidentally exported fromList functions from Data.Primitive.SmallArray --- Data/Primitive/SmallArray.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index a489122f..c690e768 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -54,8 +54,6 @@ module Data.Primitive.SmallArray , unsafeThawSmallArray , sizeofSmallArray , sizeofSmallMutableArray - , fromListN - , fromList , unsafeTraverseSmallArray ) where From 10ce00ea17d23e19128c59616ebb3e85c1932443 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 21 Mar 2018 21:02:14 -0400 Subject: [PATCH 14/29] make fromListN safer for Array, SmallArray, and ByteArray --- Data/Primitive/Array.hs | 33 +++++++++++++++++++++------------ Data/Primitive/ByteArray.hs | 16 ++++++++++++---- Data/Primitive/SmallArray.hs | 27 +++++++++++++++++++++------ test/main.hs | 15 --------------- 4 files changed, 54 insertions(+), 37 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 896f64ba..1212e717 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -500,26 +500,35 @@ unsafeTraverseArray f = \ !ary -> go 0 mary {-# INLINE unsafeTraverseArray #-} +arrayFromListN :: Int -> [a] -> Array a +arrayFromListN n l = runST $ do + sma <- newArray n (die "fromListN" "uninitialized element") + let go !ix [] = if ix == n + then return () + else die "fromListN" "list length less than specified size" + go !ix (x : xs) = if ix < n + then do + writeArray sma ix x + go (ix+1) xs + else die "fromListN" "list length greater than specified size" + go 0 l + unsafeFreezeArray sma + +arrayFromList :: [a] -> Array a +arrayFromList l = arrayFromListN (length l) l + #if MIN_VERSION_base(4,7,0) instance Exts.IsList (Array a) where type Item (Array a) = a - fromListN n l = - createArray n (die "fromListN" "mismatched size and list") $ \mi -> - let go i (x:xs) = writeArray mi i x >> go (i+1) xs - go _ [ ] = return () - in go 0 l - fromList l = Exts.fromListN (length l) l + fromListN = arrayFromListN + fromList = arrayFromList toList = toList #else fromListN :: Int -> [a] -> Array a -fromListN n l = - createArray n (die "fromListN" "mismatched size and list") $ \mi -> - let go i (x:xs) = writeArray mi i x >> go (i+1) xs - go _ [ ] = return () - in go 0 l +fromListN = arrayFromListN fromList :: [a] -> Array a -fromList l = fromListN (length l) l +fromList = arrayFromList #endif instance Functor Array where diff --git a/Data/Primitive/ByteArray.hs b/Data/Primitive/ByteArray.hs index 2e4e69c6..a1412d11 100644 --- a/Data/Primitive/ByteArray.hs +++ b/Data/Primitive/ByteArray.hs @@ -185,10 +185,14 @@ foldrByteArray f z arr = go 0 fromListN :: Prim a => Int -> [a] -> ByteArray fromListN n ys = runST $ do marr <- newByteArray (n * sizeOf (head ys)) - let go !_ [] = return () - go !ix (x : xs) = do - writeByteArray marr ix x - go (ix + 1) xs + let go !ix [] = if ix == n + then return () + else die "fromListN" "list length less than specified size" + go !ix (x : xs) = if ix < n + then do + writeByteArray marr ix x + go (ix + 1) xs + else die "fromListN" "list length greater than specified size" go 0 ys unsafeFreezeByteArray marr @@ -447,3 +451,7 @@ instance Exts.IsList ByteArray where fromList xs = fromListN (length xs) xs fromListN = fromListN #endif + +die :: String -> String -> a +die fun problem = error $ "Data.Primitive.ByteArray." ++ fun ++ ": " ++ problem + diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index c690e768..0931f3cb 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -54,6 +54,8 @@ module Data.Primitive.SmallArray , unsafeThawSmallArray , sizeofSmallArray , sizeofSmallMutableArray + , smallArrayFromList + , smallArrayFromListN , unsafeTraverseSmallArray ) where @@ -785,14 +787,27 @@ instance Monoid (SmallArray a) where instance IsList (SmallArray a) where type Item (SmallArray a) = a - fromListN n l = - createSmallArray n (die "fromListN" "mismatched size and list") $ \sma -> - fix ? 0 ? l $ \go i li -> case li of - [] -> pure () - x:xs -> writeSmallArray sma i x *> go (i+1) xs - fromList l = fromListN (length l) l + fromListN = smallArrayFromListN + fromList = smallArrayFromList toList = Foldable.toList +smallArrayFromListN :: Int -> [a] -> SmallArray a +smallArrayFromListN n l = runST $ do + sma <- newSmallArray n (die "smallArrayFromListN" "uninitialized element") + let go !ix [] = if ix == n + then return () + else die "smallArrayFromListN" "list length less than specified size" + go !ix (x : xs) = if ix < n + then do + writeSmallArray sma ix x + go (ix+1) xs + else die "smallArrayFromListN" "list length greater than specified size" + go 0 l + unsafeFreezeSmallArray sma + +smallArrayFromList :: [a] -> SmallArray a +smallArrayFromList l = smallArrayFromListN (length l) l + smallArrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> SmallArray a -> ShowS smallArrayLiftShowsPrec elemShowsPrec elemListShowsPrec p sa = showParen (p > 10) $ showString "fromListN " . shows (length sa) . showString " " diff --git a/test/main.hs b/test/main.hs index c8ea4984..95b50f24 100644 --- a/test/main.hs +++ b/test/main.hs @@ -147,18 +147,3 @@ iforM_ xs0 f = go 0 xs0 where go !_ [] = return () go !ix (x : xs) = f ix x >> go (ix + 1) xs -infixl 1 ? -(?) :: (a -> b -> c) -> (b -> a -> c) -(?) = flip -{-# INLINE (?) #-} - -smallArrayFromListN :: Int -> [a] -> SmallArray a -smallArrayFromListN n l = runST $ do - sma <- newSmallArray n (error "primitive:test, smallArrayFromListN, mismatched size and list") - fix ? 0 ? l $ \go i li -> case li of - [] -> pure () - x:xs -> writeSmallArray sma i x *> go (i+1) xs - unsafeFreezeSmallArray sma - -smallArrayFromList :: [a] -> SmallArray a -smallArrayFromList l = smallArrayFromListN (length l) l From c57ea64f9dec2bcaeee0898685989e3f85e976be Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 05:52:42 -0400 Subject: [PATCH 15/29] make compatible with transformers-0.3.0.0 again --- Data/Primitive/Array.hs | 8 ++++++ Data/Primitive/SmallArray.hs | 50 ++++++++++++++++++++++++------------ primitive.cabal | 2 +- test/main.hs | 4 +++ test/primitive-tests.cabal | 3 ++- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 1212e717..6a5e367a 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -63,7 +63,9 @@ import Data.Functor.Identity import Text.ParserCombinators.ReadP +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..)) +#endif -- | Boxed arrays data Array a = Array @@ -300,12 +302,14 @@ arrayLiftEq p a1 a2 = sizeofArray a1 == sizeofArray a2 && loop (sizeofArray a1 - instance Eq a => Eq (Array a) where a1 == a2 = arrayLiftEq (==) a1 a2 +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) instance Eq1 Array where #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftEq = arrayLiftEq #else eq1 = arrayLiftEq (==) #endif +#endif instance Eq (MutableArray s a) where ma1 == ma2 = isTrue# (sameMutableArray# (marray# ma1) (marray# ma2)) @@ -324,12 +328,14 @@ arrayLiftCompare elemCompare a1 a2 = loop 0 instance Ord a => Ord (Array a) where compare a1 a2 = arrayLiftCompare compare a1 a2 +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) instance Ord1 Array where #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftCompare = arrayLiftCompare #else compare1 = arrayLiftCompare compare #endif +#endif instance Foldable Array where -- Note: we perform the array lookups eagerly so we won't @@ -693,12 +699,14 @@ listLiftShowsPrec _ sl _ = sl instance Show a => Show (Array a) where showsPrec p a = arrayLiftShowsPrec showsPrec showList p a +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) instance Show1 Array where #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftShowsPrec = arrayLiftShowsPrec #else showsPrec1 = arrayLiftShowsPrec showsPrec showList #endif +#endif instance Read a => Read (Array a) where readsPrec p = readParen (p > 10) . readP_to_S $ do diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index 0931f3cb..69cf7313 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -91,9 +91,12 @@ import Text.Read.Lex #if !(HAVE_SMALL_ARRAY) import Data.Primitive.Array import Data.Traversable +import qualified Data.Primitive.Array as Array #endif +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..)) +#endif #if HAVE_SMALL_ARRAY data SmallArray a = SmallArray (SmallArray# a) @@ -115,9 +118,11 @@ newtype SmallArray a = SmallArray (Array a) deriving , MonadFix , Monoid , Typeable +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) , Eq1 , Ord1 , Show1 +#endif ) #if MIN_VERSION_base(4,7,0) @@ -460,12 +465,14 @@ smallArrayLiftEq p sa1 sa2 = length sa1 == length sa2 && loop (length sa1 - 1) , (# y #) <- indexSmallArray## sa2 i = p x y && loop (i-1) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) instance Eq1 SmallArray where #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftEq = smallArrayLiftEq #else eq1 = smallArrayLiftEq (==) #endif +#endif instance Eq a => Eq (SmallArray a) where sa1 == sa2 = smallArrayLiftEq (==) sa1 sa2 @@ -485,12 +492,14 @@ smallArrayLiftCompare elemCompare a1 a2 = loop 0 = elemCompare x1 x2 `mappend` loop (i+1) | otherwise = compare (length a1) (length a2) +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) instance Ord1 SmallArray where #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftCompare = smallArrayLiftCompare #else compare1 = smallArrayLiftCompare compare #endif +#endif instance Ord a => Ord (SmallArray a) where compare sa1 sa2 = smallArrayLiftCompare compare sa1 sa2 @@ -791,23 +800,6 @@ instance IsList (SmallArray a) where fromList = smallArrayFromList toList = Foldable.toList -smallArrayFromListN :: Int -> [a] -> SmallArray a -smallArrayFromListN n l = runST $ do - sma <- newSmallArray n (die "smallArrayFromListN" "uninitialized element") - let go !ix [] = if ix == n - then return () - else die "smallArrayFromListN" "list length less than specified size" - go !ix (x : xs) = if ix < n - then do - writeSmallArray sma ix x - go (ix+1) xs - else die "smallArrayFromListN" "list length greater than specified size" - go 0 l - unsafeFreezeSmallArray sma - -smallArrayFromList :: [a] -> SmallArray a -smallArrayFromList l = smallArrayFromListN (length l) l - smallArrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> SmallArray a -> ShowS smallArrayLiftShowsPrec elemShowsPrec elemListShowsPrec p sa = showParen (p > 10) $ showString "fromListN " . shows (length sa) . showString " " @@ -820,12 +812,14 @@ listLiftShowsPrec _ sl _ = sl instance Show a => Show (SmallArray a) where showsPrec p sa = smallArrayLiftShowsPrec showsPrec showList p sa +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) instance Show1 SmallArray where #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) liftShowsPrec = smallArrayLiftShowsPrec #else showsPrec1 = smallArrayLiftShowsPrec showsPrec showList #endif +#endif instance Read a => Read (SmallArray a) where readPrec = parens . prec 10 $ do @@ -854,3 +848,25 @@ instance (Typeable s, Typeable a) => Data (SmallMutableArray s a) where gunfold _ _ = die "gunfold" "SmallMutableArray" dataTypeOf _ = mkNoRepType "Data.Primitive.SmallArray.SmallMutableArray" #endif + +smallArrayFromListN :: Int -> [a] -> SmallArray a +#if HAVE_SMALL_ARRAY +smallArrayFromListN n l = runST $ do + sma <- newSmallArray n (die "smallArrayFromListN" "uninitialized element") + let go !ix [] = if ix == n + then return () + else die "smallArrayFromListN" "list length less than specified size" + go !ix (x : xs) = if ix < n + then do + writeSmallArray sma ix x + go (ix+1) xs + else die "smallArrayFromListN" "list length greater than specified size" + go 0 l + unsafeFreezeSmallArray sma +#else +smallArrayFromListN n l = SmallArray (Array.fromListN n l) +#endif + +smallArrayFromList :: [a] -> SmallArray a +smallArrayFromList l = smallArrayFromListN (length l) l + diff --git a/primitive.cabal b/primitive.cabal index 35e0b39c..13c8016b 100644 --- a/primitive.cabal +++ b/primitive.cabal @@ -52,7 +52,7 @@ Library Build-Depends: base >= 4.5 && < 4.12 , ghc-prim >= 0.2 && < 0.6 - , transformers >= 0.4 && < 0.6 + , transformers >= 0.3 && < 0.6 Ghc-Options: -O2 -Wall diff --git a/test/main.hs b/test/main.hs index 95b50f24..8b346994 100644 --- a/test/main.hs +++ b/test/main.hs @@ -43,10 +43,12 @@ main = do #if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int))) #endif +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 Array)) +#endif ] , testGroup "SmallArray" [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int))) @@ -55,10 +57,12 @@ main = do #if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int))) #endif +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 SmallArray)) +#endif ] , testGroup "ByteArray" [ lawsToTest (QCC.eqLaws (Proxy :: Proxy ByteArray)) diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index 866df731..c794e027 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -35,7 +35,8 @@ test-suite test , tasty , tasty-quickcheck , tagged - , quickcheck-classes == 0.4.1 + , transformers >= 0.3 + , quickcheck-classes == 0.4.2 ghc-options: -O2 source-repository head From fba4adef837374d0a5848f33453c8ca4fc8a0512 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 05:58:27 -0400 Subject: [PATCH 16/29] document changes in changelog --- changelog.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/changelog.md b/changelog.md index 7ecfaec6..c0a1c0c7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,18 @@ +## Changes in version 0.6.4.0 + + * Add `Eq1`, `Ord1`, and `Show1` instances for `Array` and `SmallArray`. + + * Drop support for `transformers-0.2.x.y`. + + * Improve the test suite. This includes having property tests for + typeclasses from `base` such as `Eq`, `Ord`, `Functor`, `Applicative`, + `Monad`, `IsList`, and `Monoid`. + + * Fix the broken `IsList` instance for `ByteArray`. + + * Fix the broken `Functor`, `Applicative`, and `Monad` instances for + `Array` and `SmallArray`. + ## Changes in version 0.6.3.0 * Add `PrimMonad` instances for `ContT`, `AccumT`, and `SelectT` from From 39a8404e07163cbc46ffcd5dac34792b4f0371c1 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 11:25:07 -0400 Subject: [PATCH 17/29] Bump lower bound for transformers back down to 0.2. Explain the test suite a little in a test suite readme --- changelog.md | 2 -- primitive.cabal | 2 +- test/README.md | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 test/README.md diff --git a/changelog.md b/changelog.md index c0a1c0c7..837449c7 100644 --- a/changelog.md +++ b/changelog.md @@ -2,8 +2,6 @@ * Add `Eq1`, `Ord1`, and `Show1` instances for `Array` and `SmallArray`. - * Drop support for `transformers-0.2.x.y`. - * Improve the test suite. This includes having property tests for typeclasses from `base` such as `Eq`, `Ord`, `Functor`, `Applicative`, `Monad`, `IsList`, and `Monoid`. diff --git a/primitive.cabal b/primitive.cabal index 13c8016b..5cd46386 100644 --- a/primitive.cabal +++ b/primitive.cabal @@ -52,7 +52,7 @@ Library Build-Depends: base >= 4.5 && < 4.12 , ghc-prim >= 0.2 && < 0.6 - , transformers >= 0.3 && < 0.6 + , transformers >= 0.2 && < 0.6 Ghc-Options: -O2 -Wall diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..615d01d6 --- /dev/null +++ b/test/README.md @@ -0,0 +1,17 @@ +Test Suite +======================= + +The test suite for `primitive` cannot be included in the same package +as `primitive` itself. The test suite depends on `QuickCheck`, which +transitively depends on `primitive`. To break up this dependency cycle, +the test suite lives here in its own unpublished package. + +This test suite is tested by travis. Although `primitive` supports +versions of transformers all the way back to `transformers-0.2.0.0`, +the test suite cannot be build with versions of transformers older than +`transformers-0.3.0.0`. As far as test coverage goes, this should not +be a problem since there is no CPP that treats these two versions of +transformers differently. Travis tests `transformers-0.3.0.0` with +its GHC 7.8.4 build, which provides high confidence that `primitive` +works with every version of transformers that it claims to. + From 15c2b21e45fea31742d8b6923348b7f1f289628d Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 11:27:28 -0400 Subject: [PATCH 18/29] allow future releases of quickcheck-classes to be accepted --- test/primitive-tests.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index c794e027..81b1cedd 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -36,7 +36,7 @@ test-suite test , tasty-quickcheck , tagged , transformers >= 0.3 - , quickcheck-classes == 0.4.2 + , quickcheck-classes >= 0.4.2 ghc-options: -O2 source-repository head From 2fb39406a27ddfc588fdbfe0c72c79337ea6e570 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 11:30:20 -0400 Subject: [PATCH 19/29] document the SmallArray list conversion functions --- Data/Primitive/SmallArray.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index 69cf7313..d37d0235 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -849,6 +849,8 @@ instance (Typeable s, Typeable a) => Data (SmallMutableArray s a) where dataTypeOf _ = mkNoRepType "Data.Primitive.SmallArray.SmallMutableArray" #endif +-- | Create a 'SmallArray' from a list of a known length. If the length +-- of the list does not match the given length, this throws an exception. smallArrayFromListN :: Int -> [a] -> SmallArray a #if HAVE_SMALL_ARRAY smallArrayFromListN n l = runST $ do @@ -867,6 +869,7 @@ smallArrayFromListN n l = runST $ do smallArrayFromListN n l = SmallArray (Array.fromListN n l) #endif +-- | Create a 'SmallArray' from a list. smallArrayFromList :: [a] -> SmallArray a smallArrayFromList l = smallArrayFromListN (length l) l From cb2ff9b750c6138400dce6b6d494e3ec76430b55 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 13:17:00 -0400 Subject: [PATCH 20/29] add Read1 and Show1 instances for Array and SmallArray. Test Read and Show instances in test suite. --- Data/Primitive/Array.hs | 29 +++++++++++++++++++++-------- Data/Primitive/SmallArray.hs | 32 +++++++++++++++++++++++--------- test/main.hs | 3 +++ 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 6a5e367a..34676bd0 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -64,7 +64,7 @@ import Data.Functor.Identity import Text.ParserCombinators.ReadP #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) -import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..)) +import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..),Read1(..)) #endif -- | Boxed arrays @@ -708,14 +708,27 @@ instance Show1 Array where #endif #endif +arrayLiftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (Array a) +arrayLiftReadsPrec _ listReadsPrec p = readParen (p > 10) . readP_to_S $ do + () <$ string "fromListN" + skipSpaces + n <- readS_to_P reads + skipSpaces + l <- readS_to_P listReadsPrec + return $ arrayFromListN n l + instance Read a => Read (Array a) where - readsPrec p = readParen (p > 10) . readP_to_S $ do - () <$ string "fromListN" - skipSpaces - n <- readS_to_P reads - skipSpaces - l <- readS_to_P reads - return $ fromListN n l + readsPrec = arrayLiftReadsPrec readsPrec readList + +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) +instance Read1 Array where +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) + liftReadsPrec = arrayLiftReadsPrec +#else + readsPrec1 = arrayLiftReadsPrec readsPrec readList +#endif +#endif + arrayDataType :: DataType arrayDataType = mkDataType "Data.Primitive.Array.Array" [fromListConstr] diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index d37d0235..f315775f 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -84,9 +84,7 @@ import Data.Monoid #if MIN_VERSION_base(4,9,0) import qualified Data.Semigroup as Sem #endif -import Text.ParserCombinators.ReadPrec -import Text.Read -import Text.Read.Lex +import Text.ParserCombinators.ReadP #if !(HAVE_SMALL_ARRAY) import Data.Primitive.Array @@ -95,7 +93,7 @@ import qualified Data.Primitive.Array as Array #endif #if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) -import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..)) +import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..),Read1(..)) #endif #if HAVE_SMALL_ARRAY @@ -821,12 +819,28 @@ instance Show1 SmallArray where #endif #endif +smallArrayLiftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (SmallArray a) +smallArrayLiftReadsPrec _ listReadsPrec p = readParen (p > 10) . readP_to_S $ do + () <$ string "fromListN" + skipSpaces + n <- readS_to_P reads + skipSpaces + l <- readS_to_P listReadsPrec + return $ smallArrayFromListN n l + instance Read a => Read (SmallArray a) where - readPrec = parens . prec 10 $ do - Symbol "fromListN" <- lexP - Number nu <- lexP - n <- maybe empty pure $ numberToInteger nu - fromListN (fromIntegral n) <$> readPrec + readsPrec = smallArrayLiftReadsPrec readsPrec readList + +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0) +instance Read1 SmallArray where +#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0) + liftReadsPrec = smallArrayLiftReadsPrec +#else + readsPrec1 = smallArrayLiftReadsPrec readsPrec readList +#endif +#endif + + smallArrayDataType :: DataType smallArrayDataType = diff --git a/test/main.hs b/test/main.hs index 8b346994..178aebba 100644 --- a/test/main.hs +++ b/test/main.hs @@ -40,6 +40,7 @@ main = do [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int))) , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int))) , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int))) #if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int))) #endif @@ -54,6 +55,7 @@ main = do [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int))) , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int))) , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int))) + -- , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int))) #if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int))) #endif @@ -67,6 +69,7 @@ main = do , testGroup "ByteArray" [ lawsToTest (QCC.eqLaws (Proxy :: Proxy ByteArray)) , lawsToTest (QCC.ordLaws (Proxy :: Proxy ByteArray)) + , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int))) #if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy ByteArray)) #endif From 2d00c3d1683c7496a3a128c0ddfb36c898765730 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 22 Mar 2018 13:36:43 -0400 Subject: [PATCH 21/29] mention Read1 in changelog --- changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 837449c7..b7a6678e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ ## Changes in version 0.6.4.0 - * Add `Eq1`, `Ord1`, and `Show1` instances for `Array` and `SmallArray`. + * Add `Eq1`, `Ord1`, `Show1`, and `Read1` instances for `Array` and + `SmallArray`. * Improve the test suite. This includes having property tests for typeclasses from `base` such as `Eq`, `Ord`, `Functor`, `Applicative`, From 229d0cbb191f5d0a803167b88de70f08fa12b977 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 08:27:56 -0400 Subject: [PATCH 22/29] Start testing Traversable laws. Test foldl1 and foldr1. Fix foldl1 implementation for Array and SmallArray. --- Data/Primitive/Array.hs | 2 +- Data/Primitive/SmallArray.hs | 2 +- changelog.md | 2 +- test/main.hs | 4 +++- test/primitive-tests.cabal | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs index 34676bd0..428e30cb 100644 --- a/Data/Primitive/Array.hs +++ b/Data/Primitive/Array.hs @@ -375,7 +375,7 @@ instance Foldable Array where go i = case indexArray## ary i of (# x #) | i == 0 -> x - | otherwise -> f x (go (i - 1)) + | otherwise -> f (go (i - 1)) x in if sz < 0 then die "foldl1" "empty array" else go sz diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index f315775f..6d0d10f4 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -540,7 +540,7 @@ instance Foldable SmallArray where go i = case indexSmallArray## ary i of (# x #) | i == 0 -> x - | otherwise -> f x (go (i - 1)) + | otherwise -> f (go (i - 1)) x in if sz < 0 then die "foldl1" "Empty SmallArray" else go sz diff --git a/changelog.md b/changelog.md index b7a6678e..43dd5db6 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,7 @@ * Improve the test suite. This includes having property tests for typeclasses from `base` such as `Eq`, `Ord`, `Functor`, `Applicative`, - `Monad`, `IsList`, and `Monoid`. + `Monad`, `IsList`, `Monoid`, `Foldable`, and `Traversable`. * Fix the broken `IsList` instance for `ByteArray`. diff --git a/test/main.hs b/test/main.hs index 178aebba..e453088c 100644 --- a/test/main.hs +++ b/test/main.hs @@ -49,13 +49,14 @@ main = do , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array)) , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 Array)) + , lawsToTest (QCC.traversableLaws (Proxy1 :: Proxy1 Array)) #endif ] , testGroup "SmallArray" [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int))) , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int))) , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int))) - -- , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int))) + , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int))) #if MIN_VERSION_base(4,7,0) , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int))) #endif @@ -64,6 +65,7 @@ main = do , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 SmallArray)) , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 SmallArray)) + , lawsToTest (QCC.traversableLaws (Proxy1 :: Proxy1 SmallArray)) #endif ] , testGroup "ByteArray" diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index 81b1cedd..8e58fbc5 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -36,7 +36,7 @@ test-suite test , tasty-quickcheck , tagged , transformers >= 0.3 - , quickcheck-classes >= 0.4.2 + , quickcheck-classes >= 0.4.3 ghc-options: -O2 source-repository head From 46ba5306644d533d37326ddb9231b87822e6aeb1 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 10:57:10 -0400 Subject: [PATCH 23/29] derive Read1 when shimming SmallArray --- Data/Primitive/SmallArray.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs index 6d0d10f4..a085180e 100644 --- a/Data/Primitive/SmallArray.hs +++ b/Data/Primitive/SmallArray.hs @@ -120,6 +120,7 @@ newtype SmallArray a = SmallArray (Array a) deriving , Eq1 , Ord1 , Show1 + , Read1 #endif ) From 6f83f1493c020d923c03a63c4cded9a87c3c1b47 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 11:43:46 -0400 Subject: [PATCH 24/29] For test suite, build quickcheck-classes without aeson and semigroupoids to accelerate travis builds --- .travis.yml | 3 ++- cabal.project | 3 +++ test/README.md | 8 ++++++++ test/primitive-tests.cabal | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bc245498..90662204 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,6 +74,7 @@ install: - HADDOCK=${HADDOCK-true} - INSTALLED=${INSTALLED-true} - GHCHEAD=${GHCHEAD-false} + - CABALFLAGS="--flags='quickcheck-classes -aeson -semigroupoids'" - travis_retry cabal update -v - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" - rm -fv cabal.project cabal.project.local @@ -123,7 +124,7 @@ script: - if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi # build & run tests, build benchmarks - - cabal new-build -w ${HC} ${TEST} ${BENCH} -j2 all + - cabal new-build -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} -j2 all - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi # cabal check diff --git a/cabal.project b/cabal.project index a7f0fed1..30058bb3 100644 --- a/cabal.project +++ b/cabal.project @@ -1,2 +1,5 @@ packages: . ./test +package quickcheck-classes + flags: -aeson -semigroupoids + diff --git a/test/README.md b/test/README.md index 615d01d6..059cfca4 100644 --- a/test/README.md +++ b/test/README.md @@ -6,6 +6,14 @@ as `primitive` itself. The test suite depends on `QuickCheck`, which transitively depends on `primitive`. To break up this dependency cycle, the test suite lives here in its own unpublished package. +To accelerates builds of the test suite, it is recommended to use +`cabal new-build`, which will use the pass the flags specified in +the `cabal.project` file to build `quickcheck-classes`. From the +root directory of `primitive`, run the following command to build +the test suite: + + cabal new-build test --enable-tests + This test suite is tested by travis. Although `primitive` supports versions of transformers all the way back to `transformers-0.2.0.0`, the test suite cannot be build with versions of transformers older than diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal index 8e58fbc5..91876ad1 100644 --- a/test/primitive-tests.cabal +++ b/test/primitive-tests.cabal @@ -36,7 +36,7 @@ test-suite test , tasty-quickcheck , tagged , transformers >= 0.3 - , quickcheck-classes >= 0.4.3 + , quickcheck-classes >= 0.4.4 ghc-options: -O2 source-repository head From 567b4cbc82d42746eee1545608f28780b1f367a6 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 11:50:21 -0400 Subject: [PATCH 25/29] pass cabal flags to more places --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 90662204..bf781b39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -118,14 +118,14 @@ script: - "printf 'packages: primitive-*/*.cabal primitive-tests-*/*.cabal\\n' > cabal.project" - cat cabal.project # this builds all libraries and executables (without tests/benchmarks) - - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all + - cabal new-build -w ${HC} ${CABALFLAGS} --disable-tests --disable-benchmarks all # Build with installed constraints for packages in global-db - - if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi + - if $INSTALLED; then echo cabal new-build -w ${HC} ${CABALFLAGS} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi # build & run tests, build benchmarks - cabal new-build -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} -j2 all - - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi + - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} all; fi # cabal check # Commented out due to https://github.com/haskell/cabal/issues/4551 @@ -134,7 +134,7 @@ script: # haddock - rm -rf ./dist-newstyle - - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi + - if $HADDOCK; then cabal new-haddock -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi # REGENDATA ["-o",".travis.yml","cabal.project"] # EOF From 552fc4ccb4894947db76898bbdd120ae4d44d66f Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 12:50:54 -0400 Subject: [PATCH 26/29] remove double quotes from travis environment variable --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bf781b39..4d43cf9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ install: - HADDOCK=${HADDOCK-true} - INSTALLED=${INSTALLED-true} - GHCHEAD=${GHCHEAD-false} - - CABALFLAGS="--flags='quickcheck-classes -aeson -semigroupoids'" + - CABALFLAGS=--flags='quickcheck-classes -aeson -semigroupoids' - travis_retry cabal update -v - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" - rm -fv cabal.project cabal.project.local From ba0d7b8a650d312c4bd90b8b152bf5d154be3f17 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 13:04:16 -0400 Subject: [PATCH 27/29] try to make travis work again --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4d43cf9a..75047deb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ install: - HADDOCK=${HADDOCK-true} - INSTALLED=${INSTALLED-true} - GHCHEAD=${GHCHEAD-false} - - CABALFLAGS=--flags='quickcheck-classes -aeson -semigroupoids' + - CABALFLAGS=--constraint='quickcheck-classes -aeson -semigroupoids' - travis_retry cabal update -v - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" - rm -fv cabal.project cabal.project.local From a57840dbbf4db8498f39b24dc5fd81f411919e25 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 13:25:03 -0400 Subject: [PATCH 28/29] fix cabal flags in travis file again --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 75047deb..d4f5d1a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ install: - HADDOCK=${HADDOCK-true} - INSTALLED=${INSTALLED-true} - GHCHEAD=${GHCHEAD-false} - - CABALFLAGS=--constraint='quickcheck-classes -aeson -semigroupoids' + - CABALFLAGS="--flags=\"quickcheck-classes -aeson -semigroupoids\"" - travis_retry cabal update -v - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" - rm -fv cabal.project cabal.project.local From cf9af6b16a05f5b29dba765af40f0a1e06cab7a7 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Fri, 23 Mar 2018 14:15:56 -0400 Subject: [PATCH 29/29] remove CABAL_FLAGS from travis --- .travis.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index d4f5d1a2..bc245498 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,6 @@ install: - HADDOCK=${HADDOCK-true} - INSTALLED=${INSTALLED-true} - GHCHEAD=${GHCHEAD-false} - - CABALFLAGS="--flags=\"quickcheck-classes -aeson -semigroupoids\"" - travis_retry cabal update -v - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" - rm -fv cabal.project cabal.project.local @@ -118,14 +117,14 @@ script: - "printf 'packages: primitive-*/*.cabal primitive-tests-*/*.cabal\\n' > cabal.project" - cat cabal.project # this builds all libraries and executables (without tests/benchmarks) - - cabal new-build -w ${HC} ${CABALFLAGS} --disable-tests --disable-benchmarks all + - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all # Build with installed constraints for packages in global-db - - if $INSTALLED; then echo cabal new-build -w ${HC} ${CABALFLAGS} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi + - if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi # build & run tests, build benchmarks - - cabal new-build -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} -j2 all - - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} all; fi + - cabal new-build -w ${HC} ${TEST} ${BENCH} -j2 all + - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi # cabal check # Commented out due to https://github.com/haskell/cabal/issues/4551 @@ -134,7 +133,7 @@ script: # haddock - rm -rf ./dist-newstyle - - if $HADDOCK; then cabal new-haddock -w ${HC} ${CABALFLAGS} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi + - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi # REGENDATA ["-o",".travis.yml","cabal.project"] # EOF