strict-list-0.1.7.5/0000755000000000000000000000000007346545000012334 5ustar0000000000000000strict-list-0.1.7.5/LICENSE0000644000000000000000000000204207346545000013337 0ustar0000000000000000Copyright (c) 2019, Nikita Volkov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. strict-list-0.1.7.5/library/0000755000000000000000000000000007346545000014000 5ustar0000000000000000strict-list-0.1.7.5/library/StrictList.hs0000644000000000000000000002765407346545000016456 0ustar0000000000000000-- | -- Definitions of strict linked list. -- -- Most basic operations like `fmap`, `filter`, `<*>` -- can only be implemented efficiently by producing an intermediate list in reversed order -- and then reversing it to the original order. -- These intermediate reversed functions are exposed by the API, -- because they very well may be useful for efficient implementations of data-structures built on top of list. -- E.g., the package exploits them heavily. -- -- One useful rule of thumb would be that -- whenever you see that a function has a reversed counterpart, -- that counterpart is faster and hence if you don't care about the order or -- intend to reverse the list further down the line, you should give preference to that counterpart. -- -- The typical `toList` and `fromList` conversions are provided by means of -- the `Foldable` and `IsList` instances. module StrictList where import StrictList.Prelude hiding (drop, dropWhile, reverse, take, takeWhile) -- | -- Strict linked list. data List a = Cons !a !(List a) | Nil deriving (Eq, Ord, Show, Read, Generic, Generic1, Data, Typeable) instance IsList (List a) where type Item (List a) = a fromList = reverse . fromListReversed toList = foldr (:) [] instance Semigroup (List a) where (<>) a b = case b of Nil -> a _ -> prependReversed (reverse a) b instance Monoid (List a) where mempty = Nil mappend = (<>) instance Functor List where fmap f = reverse . mapReversed f instance Foldable List where foldr step init = let loop = \case Cons head tail -> step head (loop tail) _ -> init in loop foldl' step init = let loop !acc = \case Cons head tail -> loop (step acc head) tail _ -> acc in loop init instance Traversable List where sequenceA = foldr (liftA2 Cons) (pure Nil) instance Apply List where (<.>) fList aList = apReversed (reverse fList) (reverse aList) instance Applicative List where pure a = Cons a Nil (<*>) = (<.>) instance Alt List where () = mappend instance Plus List where zero = mempty instance Alternative List where empty = zero (<|>) = () instance Bind List where (>>-) ma amb = reverse (explodeReversed amb ma) join = reverse . joinReversed instance Monad List where return = pure (>>=) = (>>-) instance MonadPlus List where mzero = empty mplus = (<|>) instance (Hashable a) => Hashable (List a) instance (NFData a) => NFData (List a) instance NFData1 List -- | -- Convert to lazy list in normal form (with all elements and spine evaluated). toListReversed :: List a -> [a] toListReversed = go [] where go !outputList = \case Cons element list -> go (element : outputList) list Nil -> outputList -- | -- Reverse the list. {-# INLINE reverse #-} reverse :: List a -> List a reverse = foldl' (flip Cons) Nil -- | -- Leave only the specified amount of elements. {-# INLINE take #-} take :: Int -> List a -> List a take amount = reverse . takeReversed amount -- | -- Leave only the specified amount of elements, in reverse order. takeReversed :: Int -> List a -> List a takeReversed = let loop !output !amount = if amount > 0 then \case Cons head tail -> loop (Cons head output) (pred amount) tail _ -> output else const output in loop Nil -- | -- Leave only the elements after the specified amount of first elements. drop :: Int -> List a -> List a drop amount = if amount > 0 then \case Cons _ tail -> drop (pred amount) tail _ -> Nil else id -- | -- Leave only the elements satisfying the predicate. {-# INLINE filter #-} filter :: (a -> Bool) -> List a -> List a filter predicate = reverse . filterReversed predicate -- | -- Leave only the elements satisfying the predicate, -- producing a list in reversed order. filterReversed :: (a -> Bool) -> List a -> List a filterReversed predicate = let loop !newList = \case Cons head tail -> if predicate head then loop (Cons head newList) tail else loop newList tail Nil -> newList in loop Nil -- | -- Leave only the first elements satisfying the predicate. {-# INLINE takeWhile #-} takeWhile :: (a -> Bool) -> List a -> List a takeWhile predicate = reverse . takeWhileReversed predicate -- | -- Leave only the first elements satisfying the predicate, -- producing a list in reversed order. takeWhileReversed :: (a -> Bool) -> List a -> List a takeWhileReversed predicate = let loop !newList = \case Cons head tail -> if predicate head then loop (Cons head newList) tail else newList _ -> newList in loop Nil -- | -- Drop the first elements satisfying the predicate. dropWhile :: (a -> Bool) -> List a -> List a dropWhile predicate = \case Cons head tail -> if predicate head then dropWhile predicate tail else Cons head tail Nil -> Nil -- | -- An optimized version of the same predicate applied to `takeWhile` and `dropWhile`. -- IOW, -- -- >span predicate list = (takeWhile predicate list, dropWhile predicate list) {-# INLINE span #-} span :: (a -> Bool) -> List a -> (List a, List a) span predicate = first reverse . spanReversed predicate -- | -- Same as `span`, only with the first list in reverse order. spanReversed :: (a -> Bool) -> List a -> (List a, List a) spanReversed predicate = let buildPrefix !prefix = \case Cons head tail -> if predicate head then buildPrefix (Cons head prefix) tail else (prefix, Cons head tail) _ -> (prefix, Nil) in buildPrefix Nil -- | -- An opposite version of `span`. I.e., -- -- >break predicate = span (not . predicate) {-# INLINE break #-} break :: (a -> Bool) -> List a -> (List a, List a) break predicate = first reverse . breakReversed predicate -- | -- Same as `break`, only with the first list in reverse order. breakReversed :: (a -> Bool) -> List a -> (List a, List a) breakReversed predicate = let buildPrefix !prefix = \case Cons head tail -> if predicate head then (prefix, Cons head tail) else buildPrefix (Cons head prefix) tail _ -> (prefix, Nil) in buildPrefix Nil -- | -- Same as @(`takeWhile` predicate . `reverse`)@. -- E.g., -- -- >>> takeWhileFromEnding (> 2) (fromList [1,4,2,3,4,5]) -- fromList [5,4,3] {-# INLINE takeWhileFromEnding #-} takeWhileFromEnding :: (a -> Bool) -> List a -> List a takeWhileFromEnding predicate = foldl' ( \newList a -> if predicate a then Cons a newList else Nil ) Nil -- | -- Same as @(`dropWhile` predicate . `reverse`)@. -- E.g., -- -- >>> dropWhileFromEnding (> 2) (fromList [1,4,2,3,4,5]) -- fromList [2,4,1] dropWhileFromEnding :: (a -> Bool) -> List a -> List a dropWhileFromEnding predicate = let loop confirmed unconfirmed = \case Cons head tail -> if predicate head then loop confirmed (Cons head unconfirmed) tail else let !newConfirmed = Cons head unconfirmed in loop newConfirmed newConfirmed tail Nil -> confirmed in loop Nil Nil -- | -- Same as @(`span` predicate . `reverse`)@. spanFromEnding :: (a -> Bool) -> List a -> (List a, List a) spanFromEnding predicate = let loop !confirmedPrefix !unconfirmedPrefix !suffix = \case Cons head tail -> if predicate head then loop confirmedPrefix (Cons head unconfirmedPrefix) (Cons head suffix) tail else let !prefix = Cons head unconfirmedPrefix in loop prefix prefix Nil tail Nil -> (suffix, confirmedPrefix) in loop Nil Nil Nil -- | -- Pattern match on list using functions. -- -- Allows to achieve all the same as `uncons` only without intermediate `Maybe`. -- -- Essentially provides the same functionality as `either` for `Either` and `maybe` for `Maybe`. {-# INLINE match #-} match :: result -> (element -> List element -> result) -> List element -> result match nil cons = \case Cons head tail -> cons head tail Nil -> nil -- | -- Get the first element and the remainder of the list if it's not empty. {-# INLINE uncons #-} uncons :: List a -> Maybe (a, List a) uncons = \case Cons head tail -> Just (head, tail) _ -> Nothing -- | -- Get the first element, if list is not empty. {-# INLINE head #-} head :: List a -> Maybe a head = \case Cons head _ -> Just head _ -> Nothing -- | -- Get the last element, if list is not empty. {-# INLINE last #-} last :: List a -> Maybe a last = let loop !previous = \case Cons head tail -> loop (Just head) tail _ -> previous in loop Nothing -- | -- Get all elements of the list but the first one. {-# INLINE tail #-} tail :: List a -> List a tail = \case Cons _ tail -> tail Nil -> Nil -- | -- Get all elements but the last one. {-# INLINE init #-} init :: List a -> List a init = reverse . initReversed -- | -- Get all elements but the last one, producing the results in reverse order. initReversed :: List a -> List a initReversed = let loop !confirmed !unconfirmed = \case Cons head tail -> loop unconfirmed (Cons head unconfirmed) tail _ -> confirmed in loop Nil Nil -- | -- Apply the functions in the left list to elements in the right one. {-# INLINE apZipping #-} apZipping :: List (a -> b) -> List a -> List b apZipping left right = apZippingReversed (reverse left) (reverse right) -- | -- Apply the functions in the left list to elements in the right one, -- producing a list of results in reversed order. apZippingReversed :: List (a -> b) -> List a -> List b apZippingReversed = let loop bList = \case Cons f fTail -> \case Cons a aTail -> loop (Cons (f a) bList) fTail aTail _ -> bList _ -> const bList in loop Nil -- ** Reversed intermediate functions used in instances ------------------------- -- | -- Construct from a lazy list in reversed order. {-# INLINE fromListReversed #-} fromListReversed :: [a] -> List a fromListReversed = foldl' (flip Cons) Nil -- | -- Add elements of the left list in reverse order -- in the beginning of the right list. {-# INLINE prependReversed #-} prependReversed :: List a -> List a -> List a prependReversed = \case Cons head tail -> prependReversed tail . Cons head Nil -> id -- | -- Map producing a list in reversed order. mapReversed :: (a -> b) -> List a -> List b mapReversed f = let loop !newList = \case Cons head tail -> loop (Cons (f head) newList) tail _ -> newList in loop Nil -- | -- Apply the functions in the left list to every element in the right one, -- producing a list of results in reversed order. {-# INLINE apReversed #-} apReversed :: List (a -> b) -> List a -> List b apReversed fList aList = foldl' (\z f -> foldl' (\z a -> Cons (f a) z) z aList) Nil fList -- | -- Use a function to produce a list of lists and then concat them sequentially, -- producing the results in reversed order. {-# INLINE explodeReversed #-} explodeReversed :: (a -> List b) -> List a -> List b explodeReversed amb = foldl' (\z -> foldl' (flip Cons) z . amb) Nil -- | -- Join (concat) producing results in reversed order. {-# INLINE joinReversed #-} joinReversed :: List (List a) -> List a joinReversed = foldl' (foldl' (flip Cons)) Nil -- | -- Map and filter elements producing results in reversed order. {-# INLINE mapMaybeReversed #-} mapMaybeReversed :: (a -> Maybe b) -> List a -> List b mapMaybeReversed f = go Nil where go !outputList = \case Cons inputElement inputTail -> case f inputElement of Just outputElement -> go (Cons outputElement outputList) inputTail Nothing -> go outputList inputTail Nil -> outputList -- | -- Keep only the present values, reversing the order. catMaybesReversed :: List (Maybe a) -> List a catMaybesReversed = go Nil where go !outputList = \case Cons inputElement inputTail -> case inputElement of Just outputElement -> go (Cons outputElement outputList) inputTail Nothing -> go outputList inputTail Nil -> outputList strict-list-0.1.7.5/library/StrictList/0000755000000000000000000000000007346545000016104 5ustar0000000000000000strict-list-0.1.7.5/library/StrictList/Prelude.hs0000644000000000000000000000720007346545000020037 0ustar0000000000000000{-# LANGUAGE CPP #-} module StrictList.Prelude ( module Exports, ) where import Control.Applicative as Exports import Control.Arrow as Exports import Control.Category as Exports import Control.Concurrent as Exports import Control.DeepSeq as Exports import Control.Exception as Exports import Control.Monad as Exports hiding (fail, forM, forM_, join, mapM, mapM_, msum, sequence, sequence_) import Control.Monad.Fail as Exports import Control.Monad.Fix as Exports hiding (fix) import Control.Monad.IO.Class as Exports import Control.Monad.ST as Exports import Data.Bifunctor.Apply as Exports hiding (first, second) import Data.Bits as Exports import Data.Bool as Exports import Data.Char as Exports import Data.Coerce as Exports import Data.Complex as Exports import Data.Data as Exports import Data.Dynamic as Exports import Data.Either as Exports import Data.Fixed as Exports import Data.Foldable as Exports hiding (toList) import Data.Function as Exports hiding (id, (.)) import Data.Functor as Exports import Data.Functor.Alt as Exports hiding (many, optional, some, ($>)) import Data.Functor.Bind as Exports hiding (($>)) import Data.Functor.Extend as Exports import Data.Functor.Identity as Exports import Data.Functor.Plus as Exports hiding (many, optional, some, ($>)) import Data.Hashable as Exports (Hashable) import Data.IORef as Exports import Data.Int as Exports import Data.Ix as Exports #if MIN_VERSION_base(4,20,0) import Data.List as Exports hiding (List, all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons, unzip) #else import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons, unzip) #endif import Data.Maybe as Exports import Data.Monoid as Exports hiding (Alt, First (..), Last (..), (<>)) import Data.Ord as Exports import Data.Proxy as Exports import Data.Ratio as Exports import Data.STRef as Exports import Data.Semigroup as Exports import Data.Semigroup.Bifoldable as Exports import Data.Semigroup.Bitraversable as Exports import Data.Semigroup.Foldable as Exports import Data.Semigroup.Traversable as Exports import Data.Semigroupoid as Exports import Data.String as Exports import Data.Traversable as Exports import Data.Tuple as Exports import Data.Unique as Exports import Data.Version as Exports import Data.Word as Exports import Debug.Trace as Exports import Foreign.ForeignPtr as Exports import Foreign.Ptr as Exports import Foreign.StablePtr as Exports import Foreign.Storable as Exports hiding (alignment, sizeOf) import GHC.Conc as Exports hiding (threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar) import GHC.Exts as Exports (IsList (..), groupWith, inline, lazy, sortWith) import GHC.Generics as Exports (Generic, Generic1) import GHC.IO.Exception as Exports import Numeric as Exports import System.Environment as Exports import System.Exit as Exports import System.IO as Exports import System.IO.Error as Exports import System.IO.Unsafe as Exports import System.Mem as Exports import System.Mem.StableName as Exports import System.Timeout as Exports import Text.Printf as Exports (hPrintf, printf) import Text.Read as Exports (Read (..), readEither, readMaybe) import Unsafe.Coerce as Exports import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, unzip, (.)) strict-list-0.1.7.5/strict-list.cabal0000644000000000000000000000325207346545000015603 0ustar0000000000000000cabal-version: 3.0 name: strict-list version: 0.1.7.5 synopsis: Strict linked list description: Implementation of strict linked list with care taken about stack. category: Data homepage: https://github.com/nikita-volkov/strict-list bug-reports: https://github.com/nikita-volkov/strict-list/issues author: Nikita Volkov maintainer: Nikita Volkov copyright: (c) 2019, Nikita Volkov license: MIT license-file: LICENSE source-repository head type: git location: git://github.com/nikita-volkov/strict-list.git common language-settings default-language: Haskell2010 default-extensions: BangPatterns DeriveDataTypeable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances LambdaCase NoImplicitPrelude RankNTypes ScopedTypeVariables StandaloneDeriving TypeApplications TypeFamilies library import: language-settings hs-source-dirs: library exposed-modules: StrictList other-modules: StrictList.Prelude build-depends: base >=4.9 && <5, deepseq >=1.4.3 && <2, hashable >=1.2 && <2, semigroupoids >=5.3 && <7, test-suite test type: exitcode-stdio-1.0 hs-source-dirs: test default-extensions: BangPatterns DeriveDataTypeable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances LambdaCase NoImplicitPrelude RankNTypes ScopedTypeVariables StandaloneDeriving TypeApplications TypeFamilies default-language: Haskell2010 main-is: Main.hs build-depends: rerebase <2, strict-list, tasty >=0.12 && <2, tasty-quickcheck >=0.9 && <0.11, strict-list-0.1.7.5/test/0000755000000000000000000000000007346545000013313 5ustar0000000000000000strict-list-0.1.7.5/test/Main.hs0000644000000000000000000002166207346545000014542 0ustar0000000000000000{-# OPTIONS_GHC -Wno-orphans #-} module Main where import qualified Data.List as Lazy import qualified Data.Maybe as Maybe import GHC.Exts as Exports (IsList (..)) import StrictList import Test.Tasty import Test.Tasty.QuickCheck import Prelude hiding (List, break, choose, drop, dropWhile, filter, head, init, last, reverse, span, tail, take, takeWhile, toList) main :: IO () main = defaultMain $ testGroup "" $ [ testProperty "toList" $ forAll strictAndLazyListGen $ \(strict, lazy) -> toList strict === lazy, testProperty "fromList" $ forAll lazyListGen $ \lazy -> toList (fromList @(List Word8) lazy) === lazy, testProperty "reverse" $ forAll strictAndLazyListGen $ \(strict, lazy) -> toList (reverse strict) === Lazy.reverse lazy, testProperty "take" $ forAll ((,) <$> arbitrary <*> strictAndLazyListGen) $ \(amount, (strict, lazy)) -> toList (take amount strict) === Lazy.take amount lazy, testProperty "drop" $ forAll ((,) <$> arbitrary <*> strictAndLazyListGen) $ \(amount, (strict, lazy)) -> toList (drop amount strict) === Lazy.drop amount lazy, testProperty "filter" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (filter predicate strict) === Lazy.filter predicate lazy, testProperty "filterReversed" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (filterReversed predicate strict) === Lazy.reverse (Lazy.filter predicate lazy), testProperty "takeWhile" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (takeWhile predicate strict) === Lazy.takeWhile predicate lazy, testProperty "takeWhileReversed" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (takeWhileReversed predicate strict) === Lazy.reverse (Lazy.takeWhile predicate lazy), testProperty "dropWhile" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (dropWhile predicate strict) === Lazy.dropWhile predicate lazy, testProperty "span" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> bimap toList toList (span predicate strict) === Lazy.span predicate lazy, testProperty "break" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> bimap toList toList (break predicate strict) === Lazy.break predicate lazy, testProperty "takeWhileFromEnding" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (takeWhileFromEnding predicate strict) === Lazy.takeWhile predicate (Lazy.reverse lazy), testProperty "dropWhileFromEnding" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> toList (dropWhileFromEnding predicate strict) === Lazy.dropWhile predicate (Lazy.reverse lazy), testProperty "spanFromEnding" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(predicate, (strict, lazy)) -> bimap toList toList (spanFromEnding predicate strict) === Lazy.span predicate (Lazy.reverse lazy), testProperty "head" $ forAll strictAndLazyListGen $ \(strict, lazy) -> head strict === listToMaybe lazy, testProperty "last" $ forAll strictAndLazyListGen $ \(strict, lazy) -> last strict === listToMaybe (Lazy.reverse lazy), testProperty "tail" $ forAll strictAndLazyListGen $ \(strict, lazy) -> toList (tail strict) === Lazy.drop 1 lazy, testProperty "init" $ forAll strictAndLazyListGen $ \(strict, lazy) -> toList (init strict) === Lazy.take (Lazy.length lazy - 1) lazy, testProperty "initReversed" $ forAll strictAndLazyListGen $ \(strict, lazy) -> toList (initReversed strict) === Lazy.reverse (Lazy.take (Lazy.length lazy - 1) lazy), testProperty "fromListReversed" $ forAll strictAndLazyListGen $ \(strict, lazy) -> toList (fromListReversed lazy) === Lazy.reverse lazy, testProperty "prependReversed" $ forAll ((,) <$> strictAndLazyListGen <*> strictAndLazyListGen) $ \((strict1, lazy1), (strict2, lazy2)) -> toList (prependReversed strict1 strict2) === Lazy.reverse lazy1 <> lazy2, testProperty "mapReversed" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(mapper, (strict, lazy)) -> toList (mapReversed mapper strict) === Lazy.reverse (fmap mapper lazy), testProperty "apReversed" $ forAll ((,) <$> strictAndLazyListGen <*> strictAndLazyListGen) $ \((strict1, lazy1), (strict2, lazy2)) -> toList (apReversed (fmap (,) strict1) strict2) === Lazy.reverse ((,) <$> lazy1 <*> lazy2), testProperty "apZippingReversed" $ forAll ((,) <$> strictAndLazyListGen <*> strictAndLazyListGen) $ \((strict1, lazy1), (strict2, lazy2)) -> toList (apZippingReversed (fmap (,) strict1) strict2) === Lazy.reverse (Lazy.zip lazy1 lazy2), testProperty "explodeReversed" $ forAll ((,) <$> strictAndLazyKleisliGen <*> strictAndLazyListGen) $ \((strictK, lazyK), (strict, lazy)) -> toList (explodeReversed strictK strict) === Lazy.reverse (lazy >>= lazyK), testProperty "fmap" $ forAll ((,) <$> predicateGen <*> strictAndLazyListGen) $ \(mapper, (strict, lazy)) -> toList (fmap mapper strict) === fmap mapper lazy, testProperty "<*>" $ forAll ((,) <$> strictAndLazyListGen <*> strictAndLazyListGen) $ \((strict1, lazy1), (strict2, lazy2)) -> toList ((,) <$> strict1 <*> strict2) === ((,) <$> lazy1 <*> lazy2), testProperty "<>" $ forAll ((,) <$> strictAndLazyListGen <*> strictAndLazyListGen) $ \((strict1, lazy1), (strict2, lazy2)) -> toList (strict1 <> strict2) === (lazy1 <> lazy2), testProperty "<|>" $ forAll ((,) <$> strictAndLazyListGen <*> strictAndLazyListGen) $ \((strict1, lazy1), (strict2, lazy2)) -> toList (strict1 <|> strict2) === (lazy1 <|> lazy2), testProperty ">>=" $ forAll ((,) <$> strictAndLazyKleisliGen <*> strictAndLazyListGen) $ \((strictK, lazyK), (strict, lazy)) -> toList (strict >>= strictK) === (lazy >>= lazyK), testProperty "foldl'" $ forAll strictAndLazyListGen $ \(strict, lazy) -> foldl' (flip (:)) [] strict === foldl' (flip (:)) [] lazy, testProperty "foldr" $ forAll strictAndLazyListGen $ \(strict, lazy) -> foldr (:) [] strict === foldr (:) [] lazy, testProperty "traverse" $ forAll strictAndLazyListGen $ \(strict, lazy) -> let fn x = if mod x 2 == 0 then Right x else Left x in fmap toList (traverse fn strict) === traverse fn lazy, testProperty "toListReversed" $ forAll strictAndLazyListGen $ \(strict, lazy) -> lazy === toListReversed (reverse strict), testProperty "mapMaybeReversed" $ forAll strictAndLazyListGen $ \(strict, lazy) -> let mapper x = if mod x 2 == 0 then Just x else Nothing in Maybe.mapMaybe mapper lazy === toListReversed (mapMaybeReversed mapper strict), testProperty "catMaybesReversed" $ \(lazy :: [Maybe Word8]) -> Maybe.catMaybes lazy === toListReversed (catMaybesReversed (fromList lazy)) ] where lazyListGen = arbitrary @[Word8] strictAndLazyListGen = do lazy <- lazyListGen return (foldr Cons Nil lazy, lazy) predicateGen = do op <- elements [(>), (>=), (==), (<=), (<)] x <- arbitrary @Word8 return (op x) strictAndLazyKleisliGen = do lazy <- sizedListGen 10 let lazyK x = fmap (+ x) lazy strictK = foldr Cons Nil . lazyK in return (strictK, lazyK) sizedListGen maxSize = do length <- choose (0, maxSize) replicateM length (arbitrary @Word8) -- Workarounds to satisfy QuickCheck's requirements, -- when we need to generate a predicate. instance Show (Word8 -> Bool) where show _ = "(Word8 -> Bool) function" instance Show (Word8 -> List Word8) where show _ = "(Word8 -> List Word8) function" instance Show (Word8 -> [Word8]) where show _ = "(Word8 -> [Word8]) function"