on master help sign in
Add the ZipList type and more tests for pure lists
0839d4fHarendra Kumar
src/Streamly/List.hs+73−49
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE PatternSynonyms #-}
4{-# LANGUAGE PatternSynonyms #-}
3{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
@@ -13,48 +15,67 @@
13-- Stability : experimental
15-- Stability : experimental
14-- Portability : GHC
16-- Portability : GHC
15--
17--
18+-- Lists are just a special case of monadic streams. The stream type @SerialT
19+-- Identity a@ can be used as a replacement for @[a]@. The 'List' type in this
16−-- The 'List' type in this module is just a newtype wrapper around the stream
20+-- module is just a newtype wrapper around @SerialT Identity@ for better type
21+-- inference when using the 'OverloadedLists' GHC extension. @List a@ provides
17−-- type @SerialT Identity@. It provides better performance compared to the
22+-- better performance compared to @[a]@. Standard list, string and list
18−-- standard Haskell lists and can be used almost as a drop-in replacement. The
23+-- comprehension syntax can be used with the 'List' type by enabling
19−-- type @List a@ is equivalent to @[a]@ with the following correspondences:
24+-- 'OverloadedLists', 'OverloadedStrings' and 'MonadComprehensions' GHC
25+-- extensions. If you want to replace streamly lists with standard lists, all
26+-- you need to do is import these definitions:
20--
27--
21−-- * constructor @[]@ corresponds to 'Nil'
28+-- @
22−-- * constructor @:@ corresponds to 'Cons'
29+-- type List = []
30+-- pattern Nil <- [] where Nil = []
31+-- pattern Cons x xs = x : xs
32+-- infixr 5 `Cons`
33+-- {-\# COMPLETE Cons, Nil #-}
34+-- @
35+--
36+-- Other than that there would be a slight difference in the 'Show' and
37+-- 'Read' strings.
38+--
39+-- Conversion to stream types is free, any stream combinator can be used on
40+-- lists by converting them to streams. However, for convenience, this module
41+-- provides combinators that work directly on the 'List' type.
23--
42--
24−-- If you use 'OverloadedLists', 'OverloadedStrings' and 'MonadComprehensions'
25−-- extensions then the standard list, string and list comprehension syntax can
26−-- be used. 'toList' and 'fromList' from the 'IsList' typeclass can be used to
27−-- convert to and from standard lists. Conversion to stream types is free.
28−-- Stream combinators can be used on lists by converting them to streams, such
29−-- as:
30--
43--
31-- @
44-- @
32−-- fromSerial $ S.map (+ 1) $ toSerial (1 \`Cons\` Nil)
45+-- List $ S.map (+ 1) $ toSerial (1 \`Cons\` Nil)
33-- @
46-- @
34--
47--
35−-- This module provides combinators that work directly on the 'List' type and
48+-- 'Data.Foldable.toList' from 'Foldable' and 'GHC.Exts.toList' and
36−-- therefore no conversion is needed.
49+-- 'GHC.Exts.fromList' from 'IsList' can also be used to convert to and from
50+-- standard lists.
37--
51--
38−-- See <docs/streamly-vs-lists.md> for more details and
52+-- See <src/docs/streamly-vs-lists.md> for more details and
39−-- <test/PureStreams.hs> for comprehensive usage examples.
53+-- <src/test/PureStreams.hs> for comprehensive usage examples.
40--
54--
41module Streamly.List
55module Streamly.List
56+ (
57+#if __GLASGOW_HASKELL__ >= 800
58+ List (.., Nil, Cons)
59+#else
42− ( List
60+ List (..)
43 , pattern Nil
61 , pattern Nil
44 , pattern Cons
62 , pattern Cons
45− , fromSerial
63+#endif
46− , toSerial
64+ , ZipList (..)
65+ , fromZipList
66+ , toZipList
47 )
67 )
48where
68where
49
69
50import Control.Arrow (second)
70import Control.Arrow (second)
51import Control.DeepSeq (NFData(..), NFData1(..))
71import Control.DeepSeq (NFData(..), NFData1(..))
52import Data.Functor.Identity (Identity, runIdentity)
72import Data.Functor.Identity (Identity, runIdentity)
73+import Data.Semigroup (Semigroup(..))
53import GHC.Exts (IsList(..), IsString(..))
74import GHC.Exts (IsList(..), IsString(..))
54−import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
55− readListPrecDefault)
56
75
57import Streamly.Streams.Serial (SerialT)
76import Streamly.Streams.Serial (SerialT)
77+import Streamly.Streams.Zip (ZipSerialM)
78+
58import qualified Streamly.Streams.Prelude as P
79import qualified Streamly.Streams.Prelude as P
59import qualified Streamly.Streams.StreamK as K
80import qualified Streamly.Streams.StreamK as K
60
81
@@ -63,23 +84,13 @@ import qualified Streamly.Streams.StreamK as K
63-- using a stream type the programmer needs to specify the Monad otherwise the
84-- using a stream type the programmer needs to specify the Monad otherwise the
64-- type remains ambiguous.
85-- type remains ambiguous.
65--
86--
66−-- | A better replacement for the standard lists in 'base'.
87+-- | @List a@ is a replacement for @[a]@.
67--
88--
68-- @since 0.5.3
89-- @since 0.5.3
69−newtype List a = List { unList :: SerialT Identity a }
90+newtype List a = List { toSerial :: SerialT Identity a }
70− deriving (Eq, Ord, IsList, NFData, NFData1
91+ deriving (Show, Read, Eq, Ord, IsList, NFData, NFData1
71− , Semigroup, Monoid, Functor, Applicative, Monad)
92+ , Semigroup, Monoid, Functor, Foldable
72−
93+ , Applicative, Traversable, Monad)
73−instance Show a => Show (List a) where {
74− showsPrec p dl = showParen (p > 10) $
75− showString "fromList " . shows (toList dl) };
76−
77−instance Read a => Read (List a) where {
78− readPrec = parens $ prec 10 $ do {
79− Ident "fromList" <- lexP;
80− dl <- readPrec;
81− return (fromList dl) };
82− readListPrec = readListPrecDefault };
83
94
84instance (a ~ Char) => IsString (List a) where
95instance (a ~ Char) => IsString (List a) where
85 {-# INLINE fromString #-}
96 {-# INLINE fromString #-}
@@ -94,34 +105,47 @@ instance (a ~ Char) => IsString (List a) where
94--
105--
95-- @since 0.5.3
106-- @since 0.5.3
96pattern Nil :: List a
107pattern Nil :: List a
97−pattern Nil <- (runIdentity . K.null . unList -> True) where
108+pattern Nil <- (runIdentity . K.null . toSerial -> True) where
98 Nil = List K.nil
109 Nil = List K.nil
99
110
111+infixr 5 `Cons`
112+
100-- | A list constructor and pattern that deconstructs a 'List' into its head
113-- | A list constructor and pattern that deconstructs a 'List' into its head
101-- and tail. Corresponds to ':' for Haskell lists.
114-- and tail. Corresponds to ':' for Haskell lists.
102--
115--
103-- @since 0.5.3
116-- @since 0.5.3
104pattern Cons :: a -> List a -> List a
117pattern Cons :: a -> List a -> List a
105pattern Cons x xs <-
118pattern Cons x xs <-
106− (fmap (second List) . runIdentity . K.uncons . unList
119+ (fmap (second List) . runIdentity . K.uncons . toSerial
107 -> Just (x, xs)) where
120 -> Just (x, xs)) where
108− Cons x xs = List $ K.cons x (unList xs)
121+ Cons x xs = List $ K.cons x (toSerial xs)
122+
123+{-# COMPLETE Nil, Cons #-}
109
124
110------------------------------------------------------------------------------
125------------------------------------------------------------------------------
111−-- Conversion to and from stream
126+-- ZipList
112------------------------------------------------------------------------------
127------------------------------------------------------------------------------
113
128
114−-- We do not expose the newtype wrapper as that would require the show
129+-- | Just like 'List' except that it has a zipping 'Applicative' instance
115−-- instance to show it which makes show and read more complicated.
130+-- and no 'Monad' instance.
131+--
132+-- @since 0.5.3
133+newtype ZipList a = ZipList { toZipSerial :: ZipSerialM Identity a }
134+ deriving (Show, Read, Eq, Ord, IsList, NFData, NFData1
135+ , Semigroup, Monoid, Functor, Applicative, Foldable)
136+
137+instance (a ~ Char) => IsString (ZipList a) where
138+ {-# INLINE fromString #-}
139+ fromString = ZipList . P.fromList
116
140
117−-- | Convert a 'Serial' stream to a 'List'.
141+-- | Convert a 'ZipList' to a regular 'List'
118--
142--
119-- @since 0.5.3
143-- @since 0.5.3
120−fromSerial :: SerialT Identity a -> List a
144+fromZipList :: ZipList a -> List a
121−fromSerial = List
145+fromZipList = List . K.adapt . toZipSerial
122
146
123−-- | Convert a 'List' to a 'Serial' stream.
147+-- | Convert a regular 'List' to a 'ZipList'
124--
148--
125-- @since 0.5.3
149-- @since 0.5.3
126−toSerial :: List a -> SerialT Identity a
150+toZipList :: List a -> ZipList a
127−toSerial = unList
151+toZipList = ZipList . K.adapt . toSerial
src/Streamly/Streams/Instances.hs+60
@@ -41,3 +41,63 @@ instance (MonadState s m CONSTRAINT) => MonadState s (STREAM m) where { \
41 put x = lift (put x); \
41 put x = lift (put x); \
42 state k = lift (state k) }
42 state k = lift (state k) }
43
43
44+------------------------------------------------------------------------------
45+-- Lists
46+------------------------------------------------------------------------------
47+
48+-- Serial streams can act like regular lists using the Identity monad
49+
50+-- XXX Show instance is 10x slower compared to read, we can do much better.
51+-- The list show instance itself is really slow.
52+
53+-- XXX The default definitions of "<" in the Ord instance etc. do not perform
54+-- well, because they do not get inlined. Need to add INLINE in Ord class in
55+-- base?
56+
57+#define LIST_INSTANCES(STREAM) \
58+instance IsList (STREAM Identity a) where { \
59+ type (Item (STREAM Identity a)) = a; \
60+ {-# INLINE fromList #-}; \
61+ fromList = P.fromList; \
62+ {-# INLINE toList #-}; \
63+ toList = runIdentity . P.toList }; \
64+ \
65+instance Eq a => Eq (STREAM Identity a) where { \
66+ {-# INLINE (==) #-}; \
67+ (==) xs ys = runIdentity $ P.eqBy (==) xs ys }; \
68+ \
69+instance Ord a => Ord (STREAM Identity a) where { \
70+ {-# INLINE compare #-}; \
71+ compare xs ys = runIdentity $ P.cmpBy compare xs ys; \
72+ {-# INLINE (<) #-}; \
73+ x < y = case compare x y of { LT -> True; _ -> False }; \
74+ {-# INLINE (<=) #-}; \
75+ x <= y = case compare x y of { GT -> False; _ -> True }; \
76+ {-# INLINE (>) #-}; \
77+ x > y = case compare x y of { GT -> True; _ -> False }; \
78+ {-# INLINE (>=) #-}; \
79+ x >= y = case compare x y of { LT -> False; _ -> True }; \
80+ {-# INLINE max #-}; \
81+ max x y = if x <= y then y else x; \
82+ {-# INLINE min #-}; \
83+ min x y = if x <= y then x else y; }; \
84+ \
85+instance Show a => Show (STREAM Identity a) where { \
86+ showsPrec p dl = showParen (p > 10) $ \
87+ showString "fromList " . shows (toList dl) }; \
88+ \
89+instance Read a => Read (STREAM Identity a) where { \
90+ readPrec = parens $ prec 10 $ do { \
91+ Ident "fromList" <- lexP; \
92+ dl <- readPrec; \
93+ return (fromList dl) }; \
94+ readListPrec = readListPrecDefault }; \
95+ \
96+instance (a ~ Char) => IsString (STREAM Identity a) where { \
97+ {-# INLINE fromString #-}; \
98+ fromString = P.fromList }; \
99+ \
100+instance NFData a => NFData (STREAM Identity a) where { rnf = rnf1 }; \
101+instance NFData1 (STREAM Identity) where { \
102+ {-# INLINE liftRnf #-}; \
103+ liftRnf r = runIdentity . P.foldl' (\_ x -> r x) () }
src/Streamly/Streams/Serial.hs+1−62
@@ -57,7 +57,7 @@ import Data.Semigroup (Semigroup(..))
57import GHC.Exts (IsList(..), IsString(..))
57import GHC.Exts (IsList(..), IsString(..))
58import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
58import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
59 readListPrecDefault)
59 readListPrecDefault)
60−import Prelude hiding (map, mapM, String)
60+import Prelude hiding (map, mapM)
61
61
62import Streamly.SVar (rstState)
62import Streamly.SVar (rstState)
63import Streamly.Streams.StreamK (IsStream(..), adapt, Stream(..))
63import Streamly.Streams.StreamK (IsStream(..), adapt, Stream(..))
@@ -351,66 +351,5 @@ instance Monad m => Monad (WSerialT m) where
351MONAD_APPLICATIVE_INSTANCE(WSerialT,)
351MONAD_APPLICATIVE_INSTANCE(WSerialT,)
352MONAD_COMMON_INSTANCES(WSerialT,)
352MONAD_COMMON_INSTANCES(WSerialT,)
353
353
354−------------------------------------------------------------------------------
355−-- Lists
356−------------------------------------------------------------------------------
357−
358−-- Serial streams can act like regular lists using the Identity monad
359−
360−-- XXX Show instance is 10x slower compared to read, we can do much better.
361−-- The list show instance itself is really slow.
362−
363−-- XXX The default definitions of "<" in the Ord instance etc. do not perform
364−-- well, because they do not get inlined. Need to add INLINE in Ord class in
365−-- base?
366−
367−#define LIST_INSTANCES(STREAM) \
368−instance IsList (STREAM Identity a) where { \
369− type (Item (STREAM Identity a)) = a; \
370− {-# INLINE fromList #-}; \
371− fromList = P.fromList; \
372− {-# INLINE toList #-}; \
373− toList = runIdentity . P.toList }; \
374− \
375−instance Eq a => Eq (STREAM Identity a) where { \
376− {-# INLINE (==) #-}; \
377− (==) xs ys = runIdentity $ P.eqBy (==) xs ys }; \
378− \
379−instance Ord a => Ord (STREAM Identity a) where { \
380− {-# INLINE compare #-}; \
381− compare xs ys = runIdentity $ P.cmpBy compare xs ys; \
382− {-# INLINE (<) #-}; \
383− x < y = case compare x y of { LT -> True; _ -> False }; \
384− {-# INLINE (<=) #-}; \
385− x <= y = case compare x y of { GT -> False; _ -> True }; \
386− {-# INLINE (>) #-}; \
387− x > y = case compare x y of { GT -> True; _ -> False }; \
388− {-# INLINE (>=) #-}; \
389− x >= y = case compare x y of { LT -> False; _ -> True }; \
390− {-# INLINE max #-}; \
391− max x y = if x <= y then y else x; \
392− {-# INLINE min #-}; \
393− min x y = if x <= y then x else y; }; \
394− \
395−instance Show a => Show (STREAM Identity a) where { \
396− showsPrec p dl = showParen (p > 10) $ \
397− showString "fromList " . shows (toList dl) }; \
398− \
399−instance Read a => Read (STREAM Identity a) where { \
400− readPrec = parens $ prec 10 $ do { \
401− Ident "fromList" <- lexP; \
402− dl <- readPrec; \
403− return (fromList dl) }; \
404− readListPrec = readListPrecDefault }; \
405− \
406−instance (a ~ Char) => IsString (STREAM Identity a) where { \
407− {-# INLINE fromString #-}; \
408− fromString = P.fromList }; \
409− \
410−instance NFData a => NFData (STREAM Identity a) where { rnf = rnf1 }; \
411−instance NFData1 (STREAM Identity) where { \
412− {-# INLINE liftRnf #-}; \
413− liftRnf r = runIdentity . P.foldl' (\_ x -> r x) () }
414−
415LIST_INSTANCES(SerialT)
354LIST_INSTANCES(SerialT)
416LIST_INSTANCES(WSerialT)
355LIST_INSTANCES(WSerialT)
src/Streamly/Streams/Zip.hs+9
@@ -6,6 +6,7 @@
6{-# LANGUAGE GeneralizedNewtypeDeriving#-}
6{-# LANGUAGE GeneralizedNewtypeDeriving#-}
7{-# LANGUAGE InstanceSigs #-}
7{-# LANGUAGE InstanceSigs #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE UndecidableInstances #-} -- XXX
10{-# LANGUAGE UndecidableInstances #-} -- XXX
10
11
11-- |
12-- |
@@ -38,7 +39,12 @@ module Streamly.Streams.Zip
38 )
39 )
39where
40where
40
41
42+import Control.DeepSeq (NFData(..), NFData1(..), rnf1)
43+import Data.Functor.Identity (Identity, runIdentity)
41import Data.Semigroup (Semigroup(..))
44import Data.Semigroup (Semigroup(..))
45+import GHC.Exts (IsList(..), IsString(..))
46+import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
47+ readListPrecDefault)
42import Prelude hiding (map, repeat, zipWith)
48import Prelude hiding (map, repeat, zipWith)
43
49
44import Streamly.Streams.StreamK (IsStream(..), Stream(..))
50import Streamly.Streams.StreamK (IsStream(..), Stream(..))
@@ -46,6 +52,7 @@ import Streamly.Streams.Async (mkAsync')
46import Streamly.Streams.Serial (map)
52import Streamly.Streams.Serial (map)
47import Streamly.SVar (MonadAsync, rstState)
53import Streamly.SVar (MonadAsync, rstState)
48
54
55+import qualified Streamly.Streams.Prelude as P
49import qualified Streamly.Streams.StreamK as K
56import qualified Streamly.Streams.StreamK as K
50
57
51#include "Instances.hs"
58#include "Instances.hs"
@@ -112,6 +119,8 @@ instance IsStream ZipSerialM where
112 (|:) :: Monad m => m a -> ZipSerialM m a -> ZipSerialM m a
119 (|:) :: Monad m => m a -> ZipSerialM m a -> ZipSerialM m a
113 m |: r = fromStream $ K.consMSerial m (toStream r)
120 m |: r = fromStream $ K.consMSerial m (toStream r)
114
121
122+LIST_INSTANCES(ZipSerialM)
123+
115instance Monad m => Functor (ZipSerialM m) where
124instance Monad m => Functor (ZipSerialM m) where
116 fmap = map
125 fmap = map
117
126
src/Streamly/String.hs+2−2
@@ -12,8 +12,8 @@
12-- type and can be used almost as a drop-in replacement, especially when used
12-- type and can be used almost as a drop-in replacement, especially when used
13-- with @OverloadedStrings@ extension, with little differences.
13-- with @OverloadedStrings@ extension, with little differences.
14--
14--
15−-- See "Streamly.List", <docs/streamly-vs-lists.md> for more details and
15+-- See "Streamly.List", <src/docs/streamly-vs-lists.md> for more details and
16−-- <test/PureStreams.hs> for comprehensive usage examples.
16+-- <src/test/PureStreams.hs> for comprehensive usage examples.
17--
17--
18--
18--
19module Streamly.String
19module Streamly.String
streamly.cabal+26−1
@@ -228,7 +228,7 @@ test-suite test
228 , exceptions >= 0.8 && < 0.11
228 , exceptions >= 0.8 && < 0.11
229 default-language: Haskell2010
229 default-language: Haskell2010
230
230
231−test-suite pure-streams
231+test-suite pure-streams-base
232 type: exitcode-stdio-1.0
232 type: exitcode-stdio-1.0
233 main-is: PureStreams.hs
233 main-is: PureStreams.hs
234 hs-source-dirs: test
234 hs-source-dirs: test
@@ -252,6 +252,31 @@ test-suite pure-streams
252 , hspec >= 2.0 && < 3
252 , hspec >= 2.0 && < 3
253 default-language: Haskell2010
253 default-language: Haskell2010
254
254
255+test-suite pure-streams-streamly
256+ type: exitcode-stdio-1.0
257+ main-is: PureStreams.hs
258+ hs-source-dirs: test
259+ cpp-options: -DUSE_STREAMLY_LIST
260+ ghc-options: -O0 -Wall -threaded -with-rtsopts=-N -fno-ignore-asserts
261+ if flag(dev)
262+ cpp-options: -DDEVBUILD
263+ ghc-options: -Wmissed-specialisations
264+ -Wall-missed-specialisations
265+ if impl(ghc >= 8.0)
266+ ghc-options: -Wcompat
267+ -Wunrecognised-warning-flags
268+ -Widentities
269+ -Wincomplete-record-updates
270+ -Wincomplete-uni-patterns
271+ -Wredundant-constraints
272+ -Wnoncanonical-monad-instances
273+ -Wnoncanonical-monadfail-instances
274+ build-depends:
275+ streamly
276+ , base >= 4.8 && < 5
277+ , hspec >= 2.0 && < 3
278+ default-language: Haskell2010
279+
255test-suite properties
280test-suite properties
256 type: exitcode-stdio-1.0
281 type: exitcode-stdio-1.0
257 main-is: Prop.hs
282 main-is: Prop.hs
test/PureStreams.hs+127−20
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE MonadComprehensions #-}
3{-# LANGUAGE MonadComprehensions #-}
3{-# LANGUAGE TypeFamilies #-}
4{-# LANGUAGE TypeFamilies #-}
@@ -12,16 +13,33 @@ module Main (main) where
12import Test.Hspec
13import Test.Hspec
13import qualified GHC.Exts as GHC
14import qualified GHC.Exts as GHC
14
15
15−import Data.Functor.Identity
16import Streamly
16import Streamly
17+
18+#ifdef USE_STREAMLY_LIST
19+import Data.Functor.Identity
17−import Streamly.List (pattern Cons, pattern Nil)
20+import Streamly.List (List(..), pattern Cons, pattern Nil, ZipList(..),
21+ fromZipList, toZipList)
18import qualified Streamly.Prelude as S
22import qualified Streamly.Prelude as S
19−import qualified Streamly.List as S
23+#else
24+import Prelude -- to suppress compiler warning
25+
26+type List = []
27+
28+pattern Nil :: [a]
29+pattern Nil <- [] where Nil = []
30+
31+pattern Cons :: a -> [a] -> [a]
32+pattern Cons x xs = x : xs
33+infixr 5 `Cons`
34+
35+{-# COMPLETE Nil, Cons #-}
36+#endif
20
37
21main :: IO ()
38main :: IO ()
22main = hspec $ do
39main = hspec $ do
40+#ifdef USE_STREAMLY_LIST
23 describe "OverloadedLists for 'SerialT Identity' type" $ do
41 describe "OverloadedLists for 'SerialT Identity' type" $ do
24− it "overloaded lists" $ do
42+ it "Overloaded lists" $ do
25 ([1..3] :: SerialT Identity Int) `shouldBe` S.fromList [1..3]
43 ([1..3] :: SerialT Identity Int) `shouldBe` S.fromList [1..3]
26 GHC.toList ([1..3] :: SerialT Identity Int) `shouldBe` [1..3]
44 GHC.toList ([1..3] :: SerialT Identity Int) `shouldBe` [1..3]
27
45
@@ -41,60 +59,149 @@ main = hspec $ do
41 [(x,y) | x <- [1..2], y <- [1..2]] `shouldBe`
59 [(x,y) | x <- [1..2], y <- [1..2]] `shouldBe`
42 ([(1,1), (1,2), (2,1), (2,2)] :: SerialT Identity (Int, Int))
60 ([(1,1), (1,2), (2,1), (2,2)] :: SerialT Identity (Int, Int))
43
61
62+ it "Foldable (sum)" $ sum (S.fromList [1..3] :: SerialT Identity Int)
63+ `shouldBe` 6
64+
65+ describe "OverloadedStrings for 'SerialT Identity' type" $ do
66+ it "overloaded strings" $ do
67+ ("hello" :: SerialT Identity Char) `shouldBe` S.fromList "hello"
68+#endif
69+
44 describe "OverloadedLists for List type" $ do
70 describe "OverloadedLists for List type" $ do
45 it "overloaded lists" $ do
71 it "overloaded lists" $ do
46− [1..3 :: Int] `shouldBe` S.fromSerial (S.fromList [1..3])
72+ ([1..3] :: List Int) `shouldBe` GHC.fromList [1..3]
47− GHC.toList ([1..3] :: S.List Int) `shouldBe` [1..3]
73+ GHC.toList ([1..3] :: List Int) `shouldBe` [1..3]
48
74
49− it "pattern constructor constructs a list" $ do
75+ it "Construct empty list" $ (Nil :: List Int) `shouldBe` []
76+
77+ it "Construct a list" $ do
78+ (Nil :: List Int) `shouldBe` []
50 ('x' `Cons` Nil) `shouldBe` ['x']
79 ('x' `Cons` Nil) `shouldBe` ['x']
51 (1 `Cons` [2 :: Int]) `shouldBe` [1,2]
80 (1 `Cons` [2 :: Int]) `shouldBe` [1,2]
81+ (1 `Cons` 2 `Cons` 3 `Cons` Nil :: List Int) `shouldBe` [1,2,3]
82+
83+ it "pattern matches" $ do
84+ case [] of
85+ Nil -> return ()
86+ _ -> expectationFailure "not reached"
87+
88+ case ['x'] of
89+ Cons 'x' Nil -> return ()
90+ _ -> expectationFailure "not reached"
52
91
53− it "pattern match on non-empty list" $
54 case [1..10 :: Int] of
92 case [1..10 :: Int] of
55 Cons x xs -> do
93 Cons x xs -> do
56 x `shouldBe` 1
94 x `shouldBe` 1
57 xs `shouldBe` [2..10]
95 xs `shouldBe` [2..10]
58 _ -> expectationFailure "not reached"
96 _ -> expectationFailure "not reached"
59
97
98+ case [1..10 :: Int] of
99+ x `Cons` y `Cons` xs -> do
100+ x `shouldBe` 1
101+ y `shouldBe` 2
102+ xs `shouldBe` [3..10]
103+ _ -> expectationFailure "not reached"
104+
60 it "Show instance" $ do
105 it "Show instance" $ do
61− show ([1..3] :: S.List Int) `shouldBe` "fromList [1,2,3]"
106+ show ([1..3] :: List Int) `shouldBe`
107+#ifdef USE_STREAMLY_LIST
108+ "List {toSerial = fromList [1,2,3]}"
109+#else
110+ "[1,2,3]"
111+#endif
62
112
63 it "Read instance" $ do
113 it "Read instance" $ do
114+ (read
115+#ifdef USE_STREAMLY_LIST
116+ "List {toSerial = fromList [1,2,3]}"
117+#else
118+ "[1,2,3]"
119+#endif
64− (read "fromList [1,2,3]" :: S.List Int) `shouldBe` [1..3]
120+ :: List Int) `shouldBe` [1..3]
65
121
66 it "Eq instance" $ do
122 it "Eq instance" $ do
67− ([1,2,3] :: S.List Int) == [1,2,3] `shouldBe` True
123+ ([1,2,3] :: List Int) == [1,2,3] `shouldBe` True
68
124
69 it "Ord instance" $ do
125 it "Ord instance" $ do
70− ([1,2,3] :: S.List Int) > [1,2,1] `shouldBe` True
126+ ([1,2,3] :: List Int) > [1,2,1] `shouldBe` True
71
127
72 it "Monad comprehension" $ do
128 it "Monad comprehension" $ do
73 [(x,y) | x <- [1..2], y <- [1..2]] `shouldBe`
129 [(x,y) | x <- [1..2], y <- [1..2]] `shouldBe`
74− ([(1,1), (1,2), (2,1), (2,2)] :: S.List (Int, Int))
130+ ([(1,1), (1,2), (2,1), (2,2)] :: List (Int, Int))
75
131
76− describe "OverloadedStrings for 'SerialT Identity' type" $ do
132+ it "Foldable (sum)" $ sum ([1..3] :: List Int) `shouldBe` 6
77− it "overloaded strings" $ do
78− ("hello" :: SerialT Identity Char) `shouldBe` S.fromList "hello"
79
133
80 describe "OverloadedStrings for List type" $ do
134 describe "OverloadedStrings for List type" $ do
81 it "overloaded strings" $ do
135 it "overloaded strings" $ do
82− "hello" `shouldBe` S.fromSerial (S.fromList "hello")
136+ ("hello" :: List Char) `shouldBe` GHC.fromList "hello"
83
137
84− it "pattern match on empty string" $
138+ it "pattern matches" $ do
85 case "" of
139 case "" of
86 Nil -> return ()
140 Nil -> return ()
87 _ -> expectationFailure "not reached"
141 _ -> expectationFailure "not reached"
88
142
89− it "pattern matches on singleton string" $
90 case "a" of
143 case "a" of
91 Cons x Nil -> x `shouldBe` 'a'
144 Cons x Nil -> x `shouldBe` 'a'
92 _ -> expectationFailure "not reached"
145 _ -> expectationFailure "not reached"
93
146
94− it "pattern matches on non-empty string" $
95 case "hello" <> "world" of
147 case "hello" <> "world" of
96 Cons x1 (Cons x2 xs) -> do
148 Cons x1 (Cons x2 xs) -> do
97 x1 `shouldBe` 'h'
149 x1 `shouldBe` 'h'
98 x2 `shouldBe` 'e'
150 x2 `shouldBe` 'e'
99 xs `shouldBe` "lloworld"
151 xs `shouldBe` "lloworld"
100 _ -> expectationFailure "not reached"
152 _ -> expectationFailure "not reached"
153+
154+#ifdef USE_STREAMLY_LIST
155+ describe "OverloadedLists for ZipList type" $ do
156+ it "overloaded lists" $ do
157+ ([1..3] :: ZipList Int) `shouldBe` GHC.fromList [1..3]
158+ GHC.toList ([1..3] :: ZipList Int) `shouldBe` [1..3]
159+
160+ it "toZipList" $ do
161+ toZipList (Nil :: List Int) `shouldBe` []
162+ toZipList ('x' `Cons` Nil) `shouldBe` ['x']
163+ toZipList (1 `Cons` [2 :: Int]) `shouldBe` [1,2]
164+ toZipList (1 `Cons` 2 `Cons` 3 `Cons` Nil :: List Int) `shouldBe` [1,2,3]
165+
166+ it "fromZipList" $ do
167+ case fromZipList [] of
168+ Nil -> return ()
169+ _ -> expectationFailure "not reached"
170+
171+ case fromZipList ['x'] of
172+ Cons 'x' Nil -> return ()
173+ _ -> expectationFailure "not reached"
174+
175+ case fromZipList [1..10 :: Int] of
176+ Cons x xs -> do
177+ x `shouldBe` 1
178+ xs `shouldBe` [2..10]
179+ _ -> expectationFailure "not reached"
180+
181+ case fromZipList [1..10 :: Int] of
182+ x `Cons` y `Cons` xs -> do
183+ x `shouldBe` 1
184+ y `shouldBe` 2
185+ xs `shouldBe` [3..10]
186+ _ -> expectationFailure "not reached"
187+
188+ it "Show instance" $ do
189+ show ([1..3] :: ZipList Int) `shouldBe`
190+ "ZipList {toZipSerial = fromList [1,2,3]}"
191+
192+ it "Read instance" $ do
193+ (read "ZipList {toZipSerial = fromList [1,2,3]}" :: ZipList Int)
194+ `shouldBe` [1..3]
195+
196+ it "Eq instance" $ do
197+ ([1,2,3] :: ZipList Int) == [1,2,3] `shouldBe` True
198+
199+ it "Ord instance" $ do
200+ ([1,2,3] :: ZipList Int) > [1,2,1] `shouldBe` True
201+
202+ it "Foldable (sum)" $ sum ([1..3] :: ZipList Int) `shouldBe` 6
203+
204+ it "Applicative Zip" $ do
205+ (,) <$> "abc" <*> [1..3] `shouldBe`
206+ ([('a',1),('b',2),('c',3)] :: ZipList (Char, Int))
207+#endif