Skip to content

Commit 89b39f6

Browse files
authored
Make Seq foldl', foldr' strict in the initial value (#1077)
This matches the behavior of strict folds on sets and maps.
1 parent 118f689 commit 89b39f6

File tree

3 files changed

+75
-47
lines changed

3 files changed

+75
-47
lines changed

containers-tests/containers-tests.cabal

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,12 +395,17 @@ test-suite seq-properties
395395
hs-source-dirs: tests
396396
main-is: seq-properties.hs
397397
type: exitcode-stdio-1.0
398+
build-depends:
399+
ChasingBottoms
398400

399401
ghc-options: -O2
400402
other-extensions:
401403
BangPatterns
402404
CPP
403405

406+
other-modules:
407+
Utils.Strictness
408+
404409
test-suite tree-properties
405410
import: test-deps, warnings
406411
default-language: Haskell2010

containers-tests/tests/seq-properties.hs

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import Control.Applicative (Applicative(..), liftA2)
2121
import Control.Arrow ((***))
2222
import Control.Monad.Trans.State.Strict
2323
import Data.Array (listArray)
24+
import Data.Coerce (coerce)
2425
import Data.Foldable (Foldable(foldl, foldl1, foldr, foldr1, foldMap, fold), toList, all, sum, foldl', foldr')
2526
import Data.Functor ((<$>), (<$))
2627
import Data.Maybe
@@ -43,8 +44,10 @@ import Control.Monad.Zip (MonadZip (..))
4344
import Control.DeepSeq (deepseq)
4445
import Control.Monad.Fix (MonadFix (..))
4546
import Test.Tasty.HUnit
47+
import Test.ChasingBottoms.IsBottom (isBottom)
4648
import qualified Language.Haskell.TH.Syntax as TH
4749

50+
import Utils.Strictness (Bot(..), Func2, applyFunc2)
4851

4952
main :: IO ()
5053
main = defaultMain $ testGroup "seq-properties"
@@ -56,11 +59,9 @@ main = defaultMain $ testGroup "seq-properties"
5659
, testProperty "(<$)" prop_constmap
5760
, testProperty "foldr" prop_foldr
5861
, testProperty "foldr'" prop_foldr'
59-
, testProperty "lazy foldr'" prop_lazyfoldr'
6062
, testProperty "foldr1" prop_foldr1
6163
, testProperty "foldl" prop_foldl
6264
, testProperty "foldl'" prop_foldl'
63-
, testProperty "lazy foldl'" prop_lazyfoldl'
6465
, testProperty "foldl1" prop_foldl1
6566
, testProperty "(==)" prop_equals
6667
, testProperty "compare" prop_compare
@@ -156,6 +157,12 @@ main = defaultMain $ testGroup "seq-properties"
156157
, testProperty "Right view pattern" prop_viewr_pat
157158
, testProperty "Right view constructor" prop_viewr_con
158159
, testProperty "stimes" prop_stimes
160+
, testGroup "strictness"
161+
[ testProperty "foldr" prop_strictness_foldr
162+
, testProperty "foldl" prop_strictness_foldl
163+
, testProperty "foldr'" prop_strictness_foldr'
164+
, testProperty "foldl'" prop_strictness_foldl'
165+
]
159166
]
160167

161168
------------------------------------------------------------------------
@@ -310,16 +317,6 @@ prop_foldr' xs =
310317
f = (:)
311318
z = []
312319

313-
prop_lazyfoldr' :: Seq () -> Property
314-
prop_lazyfoldr' xs =
315-
not (null xs) ==>
316-
foldr'
317-
(\e _ ->
318-
e)
319-
(error "Data.Sequence.foldr': should be lazy in initial accumulator")
320-
xs ===
321-
()
322-
323320
prop_foldr1 :: Seq Int -> Property
324321
prop_foldr1 xs =
325322
not (null xs) ==> foldr1 f xs == Data.List.foldr1 f (toList xs)
@@ -339,16 +336,6 @@ prop_foldl' xs =
339336
f = flip (:)
340337
z = []
341338

342-
prop_lazyfoldl' :: Seq () -> Property
343-
prop_lazyfoldl' xs =
344-
not (null xs) ==>
345-
foldl'
346-
(\_ e ->
347-
e)
348-
(error "Data.Sequence.foldl': should be lazy in initial accumulator")
349-
xs ===
350-
()
351-
352339
prop_foldl1 :: Seq Int -> Property
353340
prop_foldl1 xs =
354341
not (null xs) ==> foldl1 f xs == Data.List.foldl1 f (toList xs)
@@ -903,6 +890,42 @@ test_mfix = toList resS === resL
903890
resL :: [Int]
904891
resL = fmap ($ 12) $ mfix (\f -> [facty f, facty (+1), facty (+2)])
905892

893+
-- * Strictness tests
894+
895+
-- See Note [Testing strictness of folds] in map-strictness.hs
896+
897+
prop_strictness_foldr :: [A] -> Func2 A B (Bot B) -> Bot B -> Property
898+
prop_strictness_foldr xs fun (Bot z) =
899+
isBottom (foldr f z s) ===
900+
isBottom (foldr f z xs)
901+
where
902+
s = fromList xs
903+
f = coerce (applyFunc2 fun) :: A -> B -> B
904+
905+
prop_strictness_foldl :: [A] -> Func2 B A (Bot B) -> Bot B -> Property
906+
prop_strictness_foldl (xs) fun (Bot z) =
907+
isBottom (foldl f z s) ===
908+
isBottom (foldl f z xs)
909+
where
910+
s = fromList xs
911+
f = coerce (applyFunc2 fun) :: B -> A -> B
912+
913+
prop_strictness_foldr' :: [A] -> Func2 A B (Bot B) -> Bot B -> Property
914+
prop_strictness_foldr' xs fun (Bot z) =
915+
isBottom (foldr' f z s) ===
916+
isBottom (z `seq` foldr' f z xs)
917+
where
918+
s = fromList xs
919+
f = coerce (applyFunc2 fun) :: A -> B -> B
920+
921+
prop_strictness_foldl' :: [A] -> Func2 B A (Bot B) -> Bot B -> Property
922+
prop_strictness_foldl' xs fun (Bot z) =
923+
isBottom (foldl' f z s) ===
924+
isBottom (foldl' f z xs)
925+
where
926+
s = fromList xs
927+
f = coerce (applyFunc2 fun) :: B -> A -> B
928+
906929
-- Simple test monad
907930

908931
data M a = Action Int a

containers/src/Data/Sequence/Internal.hs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,16 +1147,16 @@ instance Foldable FingerTree where
11471147
foldlNodeN f z t = foldl f z t
11481148
{-# INLINE foldl #-}
11491149

1150-
foldr' _ z' EmptyT = z'
1151-
foldr' f' z' (Single x') = f' x' z'
1152-
foldr' f' z' (Deep _ pr' m' sf') =
1150+
foldr' _ !z' EmptyT = z'
1151+
foldr' f' !z' (Single x') = f' x' z'
1152+
foldr' f' !z' (Deep _ pr' m' sf') =
11531153
(foldrDigit' f' $! (foldrTree' (foldrNode' f') $! (foldrDigit' f' z') sf') m') pr'
11541154
where
11551155
foldrTree' :: (Node a -> b -> b) -> b -> FingerTree (Node a) -> b
1156-
foldrTree' _ z EmptyT = z
1157-
foldrTree' f z (Single x) = f x $! z
1158-
foldrTree' f z (Deep _ pr m sf) =
1159-
(foldr' f $! (foldrTree' (foldrNodeN' f) $! (foldr' f $! z) sf) m) pr
1156+
foldrTree' _ !z EmptyT = z
1157+
foldrTree' f !z (Single x) = f x z
1158+
foldrTree' f !z (Deep _ pr m sf) =
1159+
(foldr' f $! (foldrTree' (foldrNodeN' f) $! foldr' f z sf) m) pr
11601160

11611161
foldrDigit' :: (a -> b -> b) -> b -> Digit a -> b
11621162
foldrDigit' f z t = foldr' f z t
@@ -1168,17 +1168,17 @@ instance Foldable FingerTree where
11681168
foldrNodeN' f t z = foldr' f z t
11691169
{-# INLINE foldr' #-}
11701170

1171-
foldl' _ z' EmptyT = z'
1172-
foldl' f' z' (Single x') = f' z' x'
1173-
foldl' f' z' (Deep _ pr' m' sf') =
1171+
foldl' _ !z' EmptyT = z'
1172+
foldl' f' !z' (Single x') = f' z' x'
1173+
foldl' f' !z' (Deep _ pr' m' sf') =
11741174
(foldlDigit' f' $!
11751175
(foldlTree' (foldlNode' f') $! (foldlDigit' f' z') pr') m')
11761176
sf'
11771177
where
11781178
foldlTree' :: (b -> Node a -> b) -> b -> FingerTree (Node a) -> b
1179-
foldlTree' _ z EmptyT = z
1180-
foldlTree' f z (Single xs) = f z xs
1181-
foldlTree' f z (Deep _ pr m sf) =
1179+
foldlTree' _ !z EmptyT = z
1180+
foldlTree' f !z (Single xs) = f z xs
1181+
foldlTree' f !z (Deep _ pr m sf) =
11821182
(foldl' f $! (foldlTree' (foldl' f) $! foldl' f z pr) m) sf
11831183

11841184
foldlDigit' :: (b -> a -> b) -> b -> Digit a -> b
@@ -1276,16 +1276,16 @@ instance Foldable Digit where
12761276
foldl f z (Four a b c d) = (((z `f` a) `f` b) `f` c) `f` d
12771277
{-# INLINE foldl #-}
12781278

1279-
foldr' f z (One a) = f a z
1280-
foldr' f z (Two a b) = f a $! f b z
1281-
foldr' f z (Three a b c) = f a $! f b $! f c z
1282-
foldr' f z (Four a b c d) = f a $! f b $! f c $! f d z
1279+
foldr' f !z (One a) = f a z
1280+
foldr' f !z (Two a b) = f a $! f b z
1281+
foldr' f !z (Three a b c) = f a $! f b $! f c z
1282+
foldr' f !z (Four a b c d) = f a $! f b $! f c $! f d z
12831283
{-# INLINE foldr' #-}
12841284

1285-
foldl' f z (One a) = f z a
1286-
foldl' f z (Two a b) = (f $! f z a) b
1287-
foldl' f z (Three a b c) = (f $! (f $! f z a) b) c
1288-
foldl' f z (Four a b c d) = (f $! (f $! (f $! f z a) b) c) d
1285+
foldl' f !z (One a) = f z a
1286+
foldl' f !z (Two a b) = (f $! f z a) b
1287+
foldl' f !z (Three a b c) = (f $! (f $! f z a) b) c
1288+
foldl' f !z (Four a b c d) = (f $! (f $! (f $! f z a) b) c) d
12891289
{-# INLINE foldl' #-}
12901290

12911291
foldr1 _ (One a) = a
@@ -1374,12 +1374,12 @@ instance Foldable Node where
13741374
foldl f z (Node3 _ a b c) = ((z `f` a) `f` b) `f` c
13751375
{-# INLINE foldl #-}
13761376

1377-
foldr' f z (Node2 _ a b) = f a $! f b z
1378-
foldr' f z (Node3 _ a b c) = f a $! f b $! f c z
1377+
foldr' f !z (Node2 _ a b) = f a $! f b z
1378+
foldr' f !z (Node3 _ a b c) = f a $! f b $! f c z
13791379
{-# INLINE foldr' #-}
13801380

1381-
foldl' f z (Node2 _ a b) = (f $! f z a) b
1382-
foldl' f z (Node3 _ a b c) = (f $! (f $! f z a) b) c
1381+
foldl' f !z (Node2 _ a b) = (f $! f z a) b
1382+
foldl' f !z (Node3 _ a b c) = (f $! (f $! f z a) b) c
13831383
{-# INLINE foldl' #-}
13841384

13851385
instance Functor Node where

0 commit comments

Comments
 (0)