infinite-list 0.1.2 → 0.1.3
raw patch · 7 files changed
+136/−83 lines, 7 filesdep −tasty-benchdep ~base
Dependencies removed: tasty-bench
Dependency ranges changed: base
Files
- CHANGELOG.md +6/−0
- bench/Bench.hs +0/−11
- infinite-list.cabal +3/−17
- src/Data/List/Infinite.hs +41/−19
- src/Data/List/Infinite/Set.hs +46/−30
- test/Fusion.hs +4/−1
- test/Properties.hs +36/−5
CHANGELOG.md view
@@ -1,3 +1,9 @@+# 0.1.3++* Add `mapAccumL'`.+* Make `scanl'` stricter by forcing the initial element.+ Cf. https://github.com/haskell/core-libraries-committee/issues/335+ # 0.1.2 * Add `heteroZip` and `heteroZipWith`.
− bench/Bench.hs
@@ -1,11 +0,0 @@-{-# LANGUAGE PostfixOperators #-}--module Main where---- import qualified Data.List.Infinite as Inf-import Test.Tasty.Bench--main :: IO ()-main = defaultMain- [- ]
infinite-list.cabal view
@@ -1,14 +1,14 @@ cabal-version: 2.2 name: infinite-list-version: 0.1.2+version: 0.1.3 license: BSD-3-Clause license-file: LICENSE maintainer: andrew.lelechenko@gmail.com author: Bodigrim tested-with: ghc ==8.2.2 ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.4- ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.8 ghc ==9.4.8 ghc ==9.6.5- ghc ==9.8.4 ghc ==9.10.1 ghc ==9.12.1+ ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.8 ghc ==9.4.8 ghc ==9.6.7+ ghc ==9.8.4 ghc ==9.10.3 ghc ==9.12.2 ghc ==9.14.1 homepage: https://github.com/Bodigrim/infinite-list synopsis: Infinite lists@@ -94,17 +94,3 @@ if impl(ghc <9.2) buildable: False--benchmark infinite-bench- type: exitcode-stdio-1.0- main-is: Bench.hs- hs-source-dirs: bench- default-language: Haskell2010- ghc-options: -Wall- build-depends:- base,- infinite-list,- tasty-bench-- if impl(ghc >=8.6)- ghc-options: -fproc-alignment=64
src/Data/List/Infinite.hs view
@@ -45,6 +45,7 @@ scanl', scanl1, mapAccumL,+ mapAccumL', traverse_, for_, @@ -178,7 +179,7 @@ import GHC.Exts (oneShot) import qualified GHC.Exts import Numeric.Natural (Natural)-import Prelude (Bool (..), Enum, Int, Integer, Integral, Maybe (..), Traversable, Word, const, enumFrom, enumFromThen, flip, fromIntegral, id, maxBound, minBound, not, otherwise, snd, uncurry, (&&), (+), (-), (.), (||))+import Prelude (Bool (..), Enum, Int, Integer, Integral, Maybe (..), Traversable, Word, const, enumFrom, enumFromThen, flip, fromIntegral, id, maxBound, minBound, not, otherwise, seq, snd, uncurry, (&&), (+), (-), (.), (||)) import Data.List.Infinite.Internal import qualified Data.List.Infinite.Set as Set@@ -371,23 +372,24 @@ -- | @since 0.1.2 instance MonadFix Infinite where mfix f = map (\(!n) -> fix $ head . genericDrop n . f) ((...) (0 :: Natural))- -- To put it simply, mfix f !! n = fix ((!! n) . f)- --- -- How to derive it? As in Section 1.4 of Erkok's thesis,- -- we can start by putting mfix f = fix (>>= f).- --- -- mfix f !! n- -- = fix (>>= f) !! n- -- = [by definition of fix, fix g = g (fix g)]- -- = (fix (>>= f) >>= f) !! n- -- = [by the choice of >>= above, (xs >>= g) !! n = g (xs !! n) !! n]- -- = f (fix (>>= f) !! n) !! n- -- = ((!! n) . f) (fix (>>= f) !! n)- -- = [restoring mfix from fix]- -- = ((!! n) . f) (mfix f !! n)- --- -- Then mfix f !! n = fix ((!! n) . f). +-- To put it simply, mfix f !! n = fix ((!! n) . f)+--+-- How to derive it? As in Section 1.4 of Erkok's thesis,+-- we can start by putting mfix f = fix (>>= f).+--+-- mfix f !! n+-- = fix (>>= f) !! n+-- = [by definition of fix, fix g = g (fix g)]+-- = (fix (>>= f) >>= f) !! n+-- = [by the choice of >>= above, (xs >>= g) !! n = g (xs !! n) !! n]+-- = f (fix (>>= f) !! n) !! n+-- = ((!! n) . f) (fix (>>= f) !! n)+-- = [restoring mfix from fix]+-- = ((!! n) . f) (mfix f !! n)+--+-- Then mfix f !! n = fix ((!! n) . f).+ -- | Get the first elements of an infinite list. head :: Infinite a -> a head (x :< _) = x@@ -567,7 +569,7 @@ -- | Same as 'Data.List.Infinite.scanl', but strict in accumulator. scanl' :: (b -> a -> b) -> b -> Infinite a -> Infinite b-scanl' f z0 = (z0 :<) . flip (foldr (\x acc z -> let !fzx = f z x in fzx :< acc fzx)) z0+scanl' f !z0 = (z0 :<) . flip (foldr (\x acc z -> let !fzx = f z x in fzx :< acc fzx)) z0 scanlFB' :: (elt' -> elt -> elt') -> (elt' -> lst -> lst) -> elt -> (elt' -> lst) -> elt' -> lst scanlFB' f cons = \elt g -> oneShot (\x -> let !elt' = f x elt in elt' `cons` g elt')@@ -579,7 +581,7 @@ {-# RULES "scanl'" [~1] forall f a bs. scanl' f a bs =- build (\cons -> a `cons` foldr (scanlFB' f cons) bs a)+ build (\cons -> a `seq` a `cons` foldr (scanlFB' f cons) bs a) "scanlList'" [1] forall f (a :: a) bs. foldr (scanlFB' f (:<)) bs a = tail (scanl' f a bs)@@ -619,6 +621,26 @@ "mapAccumLList" [1] forall f s xs. foldr (mapAccumLFB f) xs s = mapAccumL f s xs+ #-}++-- | Same as 'mapAccumL', but strict in accumulator.+mapAccumL' :: (acc -> x -> (acc, y)) -> acc -> Infinite x -> Infinite y+mapAccumL' f = flip (foldr (\x acc !s -> let (s', y) = f s x in y :< acc s'))++mapAccumL'FB :: (acc -> x -> (acc, y)) -> x -> (acc -> Infinite y) -> acc -> Infinite y+mapAccumL'FB f = \x r -> oneShot (\(!s) -> let (s', y) = f s x in y :< r s')++{-# NOINLINE [1] mapAccumL' #-}++{-# INLINE [0] mapAccumL'FB #-}++{-# RULES+"mapAccumL'" [~1] forall f s xs.+ mapAccumL' f s xs =+ foldr (mapAccumL'FB f) xs s+"mapAccumL'List" [1] forall f s xs.+ foldr (mapAccumL'FB f) xs s =+ mapAccumL' f s xs #-} -- | Generate an infinite list of repeated applications.
src/Data/List/Infinite/Set.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-} -- |@@ -10,12 +11,11 @@ insert, ) where -data Color = Red | Black- deriving (Show)- -- | Okasaki red-black tree.-data Set a = Empty | Node !Color !(Set a) !a !(Set a)- deriving (Show)+data Set a+ = Empty+ | Red !(Set a) !a !(Set a)+ | Black !(Set a) !a !(Set a) empty :: Set a empty = Empty@@ -23,39 +23,55 @@ member :: (a -> a -> Ordering) -> a -> Set a -> Bool member cmp = member' where- member' x = go+ member' !x = go where go = \case Empty -> False- Node _ left center right -> case cmp x center of- LT -> go left- EQ -> True- GT -> go right+ Red left center right -> whereToGo left center right+ Black left center right -> whereToGo left center right + whereToGo left center right = case x `cmp` center of+ LT -> go left+ EQ -> True+ GT -> go right+{-# INLINE member #-}+ insert :: (a -> a -> Ordering) -> a -> Set a -> Set a insert cmp = insert' where- insert' x = blacken . go+ insert' !x = blacken . go where- go = \case- Empty -> Node Red Empty x Empty- Node color left center right -> case cmp x center of- LT -> balance color (go left) center right- EQ -> Node color left center right- GT -> balance color left center (go right)+ go node = case node of+ Empty -> Red Empty x Empty+ Red left center right -> case x `cmp` center of+ LT -> Red (go left) center right+ EQ -> node+ GT -> Red left center (go right)+ Black left center right -> case x `cmp` center of+ LT -> balanceLeft (go left) center right+ EQ -> node+ GT -> balanceRight left center (go right) - blacken = \case+ blacken node = case node of Empty -> Empty- Node _ left center right -> Node Black left center right+ Red left center right -> Black left center right+ Black {} -> node+{-# INLINE insert #-} -balance :: Color -> Set a -> a -> Set a -> Set a-balance Black (Node Red (Node Red a b c) d e) f g =- Node Red (Node Black a b c) d (Node Black e f g)-balance Black (Node Red a b (Node Red c d e)) f g =- Node Red (Node Black a b c) d (Node Black e f g)-balance Black a b (Node Red (Node Red c d e) f g) =- Node Red (Node Black a b c) d (Node Black e f g)-balance Black a b (Node Red c d (Node Red e f g)) =- Node Red (Node Black a b c) d (Node Black e f g)-balance color left center right =- Node color left center right+balanceLeft :: Set a -> a -> Set a -> Set a+balanceLeft (Red (Red a b c) d e) f g =+ Red (Black a b c) d (Black e f g)+balanceLeft (Red a b (Red c d e)) f g =+ Red (Black a b c) d (Black e f g)+balanceLeft left center right =+ Black left center right+{-# INLINE balanceLeft #-}++balanceRight :: Set a -> a -> Set a -> Set a+balanceRight a b (Red (Red c d e) f g) =+ Red (Black a b c) d (Black e f g)+balanceRight a b (Red c d (Red e f g)) =+ Red (Black a b c) d (Black e f g)+balanceRight left center right =+ Black left center right+{-# INLINE balanceRight #-}
test/Fusion.hs view
@@ -84,11 +84,13 @@ mapAccumLRepeat n = I.mapAccumL (\acc x -> (acc, fromIntegral x)) 'q' (I.repeat (fromIntegral n :: Word)) +mapAccumLRepeat' :: Int -> Infinite Int+mapAccumLRepeat' n =+ I.mapAccumL' (\acc x -> (acc, fromIntegral x)) 'q' (I.repeat (fromIntegral n :: Word)) takeFilterIterate :: [Int] takeFilterIterate = I.take 100 $ I.filter odd $ I.iterate (+ 1) 0 - sumTakeFilterIterate :: Int sumTakeFilterIterate = sum $ I.take 100 $ I.filter odd $ I.iterate (+ 1) 0 @@ -259,6 +261,7 @@ , $(inspectTest $ 'foldrCycle `hasNoType` ''Infinite) , $(inspectTest $ 'foldrWordsCycle `hasNoType` ''NonEmpty) , $(inspectTest $ 'mapAccumLRepeat `hasNoType` ''Word)+ , $(inspectTest $ 'mapAccumLRepeat' `hasNoType` ''Word) , $(inspectTest $ 'takeFilterIterate `hasNoType` ''Infinite) , $(inspectTest $ 'sumTakeFilterIterate `hasNoTypes` [''Infinite, ''[]])
test/Properties.hs view
@@ -2,6 +2,7 @@ -- Copyright: (c) 2022 Bodigrim -- Licence: BSD3 +{-# LANGUAGE CPP #-} {-# LANGUAGE PostfixOperators #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-}@@ -23,6 +24,7 @@ import Test.Tasty.QuickCheck as QC import Control.Applicative+import Control.Exception import Control.Monad import Control.Monad.Fix (mfix) import Data.Bifunctor@@ -44,8 +46,10 @@ arbitrary = (:<) <$> arbitrary <*> arbitrary shrink = const [] +#if !MIN_VERSION_QuickCheck(2,17,0) instance Arbitrary a => Arbitrary (NonEmpty a) where arbitrary = (:|) <$> arbitrary <*> arbitrary+#endif trim :: Infinite a -> [a] trim = I.take 10@@ -214,18 +218,29 @@ , testProperty "scanl" $ \(curry . applyFun -> (f :: Word -> Int -> Word)) s (Blind xs) -> trim1 (I.scanl f s xs) === L.scanl f s (trim xs)- , testProperty "scanl laziness" $ once $+ , testProperty "scanl laziness 1" $ once $ I.head (I.scanl undefined 'q' undefined) === 'q'+ , testProperty "scanl laziness 2" $ once $+ I.head (I.tail (I.scanl (const (const 'q')) undefined (I.repeat 'z'))) === 'q'+ , testProperty "scanl'" $ \(curry . applyFun -> (f :: Word -> Int -> Word)) s (Blind xs) -> trim1 (I.scanl' f s xs) === L.scanl' f s (trim xs)- , testProperty "scanl' laziness" $ once $+ , testProperty "scanl' laziness 1" $ once $ I.head (I.scanl' undefined 'q' undefined) === 'q'+ , testProperty "scanl' laziness 2" $ once $ ioProperty $ do+ x <- try $ evaluate $ I.scanl' (const (const 'q')) undefined (I.repeat 'z')+ pure $ case x of+ Left (_ :: SomeException) -> True+ _ -> False+ , testProperty "scanl1" $ \(curry . applyFun -> (f :: Int -> Int -> Int)) (Blind xs) -> trim (I.scanl1 f xs) === L.scanl1 f (trim xs)- , testProperty "scanl1 laziness" $ once $+ , testProperty "scanl1 laziness 1" $ once $ I.head (I.scanl1 undefined ('q' :< undefined)) === 'q'+ , testProperty "scanl1 laziness 2" $ once $+ I.head (I.tail (I.scanl1 (const (const 'q')) (undefined :< I.repeat 'z'))) === 'q' , testProperty "mapAccumL" $ \(curry . applyFun -> (f :: Bool -> Int -> (Bool, Word))) (Blind xs) ->@@ -233,16 +248,32 @@ , testProperty "mapAccumL laziness" $ once $ I.head (I.mapAccumL (\_ x -> (undefined, x)) undefined ('q' :< undefined)) === 'q' + , testProperty "mapAccumL'" $+ \(curry . applyFun -> (f :: Bool -> Int -> (Bool, Word))) (Blind xs) ->+ trim (I.mapAccumL' f False xs) === snd (L.mapAccumL f False (trim xs))+ , testProperty "iterate" $ \(applyFun -> (f :: Int -> Int)) s -> trim (I.iterate f s) === L.take 10 (L.iterate f s)- , testProperty "iterate laziness" $ once $+ , testProperty "iterate laziness 1" $ once $ I.head (I.iterate undefined 'q') === 'q'+ , testProperty "iterate laziness 2" $ once $+ I.head (I.tail (I.iterate (\c -> if c == 'r' then undefined else 'r') 'q')) === 'r'+ , testProperty "iterate laziness 3" $ once $+ I.iterate (const 'q') undefined `seq` () === ()+ , testProperty "iterate'" $ \(applyFun -> (f :: Int -> Int)) s -> trim (I.iterate' f s) === L.take 10 (L.iterate f s)- , testProperty "iterate' laziness" $ once $+ , testProperty "iterate' laziness 1" $ once $ I.head (I.iterate' undefined 'q') === 'q'+ , testProperty "iterate' laziness 2" $ once $+ I.head (I.tail (I.iterate' (\c -> if c == 'r' then undefined else 'r') 'q')) === 'r'+ , testProperty "iterate' laziness 3" $ once $ ioProperty $ do+ xs <- try $ evaluate $ I.iterate' (const 'q') undefined+ pure $ case xs of+ Left (_ :: SomeException) -> True+ _ -> False , testProperty "repeat" $ \(s :: Int) ->