@@ -12,8 +12,10 @@ module Streamly.Test.Data.Scanl.Combinators (main) where
1212import Data.Int (Int64 )
1313import Data.Semigroup (Sum (.. ))
1414import qualified Streamly.Internal.Data.MutArray as MArray
15+ import qualified Streamly.Internal.Data.Pipe as Pipe
1516import qualified Streamly.Internal.Data.Scanl as F
1617import qualified Streamly.Internal.Data.Stream as Stream
18+ import qualified Streamly.Internal.Data.Unfold as Unfold
1719
1820import qualified Prelude
1921import Prelude hiding (maximum , minimum , product , sum , mconcat , foldMap , maybe )
@@ -26,13 +28,165 @@ import Test.QuickCheck (Gen, Property, choose, forAll, listOf1)
2628
2729#include "Streamly/Test/Data/Scanl/CommonCombinators.hs"
2830
31+ -------------------------------------------------------------------------------
32+ -- Scanl-only tests (these combinators are not exported by the Fold module, or
33+ -- their scan output cannot be shared via the common 'check' harness).
34+ -------------------------------------------------------------------------------
35+
36+ -- 'compose' scans the input through the left scan and feeds each of its outputs
37+ -- (including the initial extract) to the right scan.
38+ composeS :: Expectation
39+ composeS =
40+ check (F. compose F. sum F. toList) ([1 , 2 , 3 ] :: [Int ])
41+ [[0 ], [0 , 1 ], [0 , 1 , 3 ], [0 , 1 , 3 , 6 ]]
42+
43+ -- 'composeMany' restarts the left scan with a fresh state each time it
44+ -- terminates. Here the left scan (take 2 sum) emits a running sum of every two
45+ -- inputs which the right scan (sum) accumulates.
46+ composeManyS :: Expectation
47+ composeManyS =
48+ check (F. composeMany (F. take 2 F. sum ) F. sum ) ([1 , 2 , 3 , 4 , 5 ] :: [Int ])
49+ [0 , 1 , 4 , 7 , 14 , 19 ]
50+
51+ -- 'with' adapts a stateful combinator (here 'indexed') so that the supplied
52+ -- predicate also sees the state (the index). This keeps elements at even
53+ -- indices.
54+ withS :: Expectation
55+ withS =
56+ check (F. with F. indexed F. filter (even . fst ) F. toList) " abcde"
57+ [" " , " a" , " a" , " ac" , " ac" , " ace" ]
58+
59+ pipeS :: Expectation
60+ pipeS =
61+ check (F. pipe (Pipe. map (* 2 )) F. sum ) ([1 , 2 , 3 ] :: [Int ]) [0 , 2 , 6 , 12 ]
62+
63+ topByS :: Expectation
64+ topByS =
65+ check (F. rmapM MArray. toList (F. topBy compare 3 )) ([5 , 1 , 4 , 2 , 3 ] :: [Int ])
66+ [[] , [5 ], [5 , 1 ], [5 , 4 , 1 ], [5 , 4 , 2 ], [5 , 4 , 3 ]]
67+
68+ bottomByS :: Expectation
69+ bottomByS =
70+ check (F. rmapM MArray. toList (F. bottomBy compare 3 )) ([5 , 1 , 4 , 2 , 3 ] :: [Int ])
71+ [[] , [5 ], [1 , 5 ], [1 , 4 , 5 ], [1 , 2 , 4 ], [1 , 2 , 3 ]]
72+
73+ indexingWithS :: Expectation
74+ indexingWithS =
75+ check (F. indexingWith 0 (+ 2 )) " abc"
76+ [Nothing , Just (0 , ' a' ), Just (2 , ' b' ), Just (4 , ' c' )]
77+
78+ indexingS :: Expectation
79+ indexingS =
80+ check F. indexing " abc"
81+ [Nothing , Just (0 , ' a' ), Just (1 , ' b' ), Just (2 , ' c' )]
82+
83+ indexingRevS :: Expectation
84+ indexingRevS =
85+ check (F. indexingRev 5 ) " abc"
86+ [Nothing , Just (5 , ' a' ), Just (4 , ' b' ), Just (3 , ' c' )]
87+
88+ takingEndByUS :: Expectation
89+ takingEndByUS =
90+ check (F. takingEndBy_ (== 3 )) ([1 , 2 , 3 , 4 , 5 ] :: [Int ])
91+ [Nothing , Just 1 , Just 2 , Nothing ]
92+
93+ mapMaybeMS :: Expectation
94+ mapMaybeMS =
95+ check
96+ (F. mapMaybeM (\ x -> return (if even x then Just x else Nothing )) F. toList)
97+ ([1 , 2 , 3 , 4 ] :: [Int ])
98+ [[] , [] , [2 ], [2 ], [2 , 4 ]]
99+
100+ -- An Unfold that streams the elements of an input list.
101+ unfoldList :: Monad m => Unfold. Unfold m [a ] a
102+ unfoldList =
103+ Unfold. unfoldrM
104+ (\ xs -> return (case xs of { [] -> Nothing ; (y: ys) -> Just (y, ys) }))
105+
106+ unfoldEachS :: Expectation
107+ unfoldEachS =
108+ check (F. unfoldEach unfoldList F. toList) ([[1 , 2 ], [3 ], [4 , 5 ]] :: [[Int ]])
109+ [[] , [1 , 2 ], [1 , 2 , 3 ], [1 , 2 , 3 , 4 , 5 ]]
110+
111+ unfoldManyS :: Expectation
112+ unfoldManyS =
113+ check (F. unfoldMany unfoldList F. toList) ([[1 , 2 ], [3 ], [4 , 5 ]] :: [[Int ]])
114+ [[] , [1 , 2 ], [1 , 2 , 3 ], [1 , 2 , 3 , 4 , 5 ]]
115+
116+ -- 'defaultSalt' is the default salt used by 'rollingHash'. It is part of the
117+ -- output contract, so the test duplicates the constant rather than importing it.
118+ defaultSaltS :: Expectation
119+ defaultSaltS = F. defaultSalt `shouldBe` (- 2578643520546668380 :: Int64 )
120+
121+ teeS :: Expectation
122+ teeS =
123+ check (F. tee F. sum F. length ) ([1 , 2 , 3 ] :: [Int ])
124+ [(0 , 0 ), (1 , 1 ), (3 , 2 ), (6 , 3 )]
125+
126+ -- Unlike the Fold 'partition' which returns the tuple of both branch results, a
127+ -- Scanl emits a single interleaved value per input: the just-updated branch.
128+ partitionByS :: Expectation
129+ partitionByS =
130+ check
131+ (F. partitionBy (\ x -> if odd x then Left x else Right x) F. length F. length )
132+ ([1 , 2 , 3 , 4 , 5 ] :: [Int ])
133+ [0 , 1 , 1 , 2 , 2 , 3 ]
134+
135+ partitionByMS :: Expectation
136+ partitionByMS =
137+ check
138+ (F. partitionByM
139+ (\ x -> return (if odd x then Left x else Right x)) F. length F. length )
140+ ([1 , 2 , 3 , 4 , 5 ] :: [Int ])
141+ [0 , 1 , 1 , 2 , 2 , 3 ]
142+
143+ partitionS :: Expectation
144+ partitionS =
145+ check (F. partition F. toList F. toList)
146+ ([Left 1 , Right 2 , Left 3 , Right 4 ] :: [Either Int Int ])
147+ [[] , [1 ], [2 ], [1 , 3 ], [2 , 4 ]]
148+
149+ -------------------------------------------------------------------------------
150+ -- Deprecated combinators (aliases for compose / composeMany)
151+ -------------------------------------------------------------------------------
152+
153+ scanlS :: Expectation
154+ scanlS =
155+ check (F. scanl F. sum F. toList) ([1 , 2 , 3 ] :: [Int ])
156+ [[0 ], [0 , 1 ], [0 , 1 , 3 ], [0 , 1 , 3 , 6 ]]
157+
158+ scanlManyS :: Expectation
159+ scanlManyS =
160+ check (F. scanlMany (F. take 2 F. sum ) F. sum ) ([1 , 2 , 3 , 4 , 5 ] :: [Int ])
161+ [0 , 1 , 4 , 7 , 14 , 19 ]
162+
29163moduleName :: String
30164moduleName = " Data.Scanl.Combinators"
31165
32166main :: IO ()
33167main = hspec $
34- describe moduleName $
168+ describe moduleName $ do
35169 describe " common" commonCombinatorsSpec
36170
37171 -- Before adding any tests here consider if it can be added to the
38172 -- common tests above.
173+ it " compose" composeS
174+ it " composeMany" composeManyS
175+ it " with" withS
176+ it " pipe" pipeS
177+ it " topBy" topByS
178+ it " bottomBy" bottomByS
179+ it " indexingWith" indexingWithS
180+ it " indexing" indexingS
181+ it " indexingRev" indexingRevS
182+ it " takingEndBy_" takingEndByUS
183+ it " mapMaybeM" mapMaybeMS
184+ it " unfoldEach" unfoldEachS
185+ it " unfoldMany" unfoldManyS
186+ it " defaultSalt" defaultSaltS
187+ it " tee" teeS
188+ it " partitionBy" partitionByS
189+ it " partitionByM" partitionByMS
190+ it " partition" partitionS
191+ it " scanl" scanlS
192+ it " scanlMany" scanlManyS
0 commit comments