on master help sign in
Add the ZipList type and more tests for pure lists
commit0839d4f6f12590168c45d48b61b9fb15a89f342b authorHarendra Kumar <harendra.kumar@gmail.com> dateSat 17 Nov 2018, 08:21 (8 years ago) parentee0c73b
7 files changed+298−134
src/Streamly/List.hs+73−49 src/Streamly/Streams/Instances.hs+60 src/Streamly/Streams/Serial.hs+1−62 src/Streamly/Streams/Zip.hs+9 src/Streamly/String.hs+2−2 streamly.cabal+26−1 test/PureStreams.hs+127−20
src/Streamly/List.hs+73−49
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
13{-# LANGUAGE GeneralizedNewtypeDeriving #-}
24{-# LANGUAGE PatternSynonyms #-}
35{-# LANGUAGE TypeFamilies #-}
@@ -13,48 +15,67 @@
1315-- Stability : experimental
1416-- Portability : GHC
1517--
16−-- The 'List' type in this module is just a newtype wrapper around the stream
17−-- type @SerialT Identity@. It provides better performance compared to the
18−-- standard Haskell lists and can be used almost as a drop-in replacement. The
19−-- type @List a@ is equivalent to @[a]@ with the following correspondences:
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
20+-- module is just a newtype wrapper around @SerialT Identity@ for better type
21+-- inference when using the 'OverloadedLists' GHC extension. @List a@ provides
22+-- better performance compared to @[a]@. Standard list, string and list
23+-- comprehension syntax can be used with the 'List' type by enabling
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:
2027--
21−-- * constructor @[]@ corresponds to 'Nil'
22−-- * constructor @:@ corresponds to 'Cons'
28+-- @
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.
2342--
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:
3043--
3144-- @
32−-- fromSerial $ S.map (+ 1) $ toSerial (1 \`Cons\` Nil)
45+-- List $ S.map (+ 1) $ toSerial (1 \`Cons\` Nil)
3346-- @
3447--
35−-- This module provides combinators that work directly on the 'List' type and
36−-- therefore no conversion is needed.
48+-- 'Data.Foldable.toList' from 'Foldable' and 'GHC.Exts.toList' and
49+-- 'GHC.Exts.fromList' from 'IsList' can also be used to convert to and from
50+-- standard lists.
3751--
38−-- See <docs/streamly-vs-lists.md> for more details and
39−-- <test/PureStreams.hs> for comprehensive usage examples.
52+-- See <src/docs/streamly-vs-lists.md> for more details and
53+-- <src/test/PureStreams.hs> for comprehensive usage examples.
4054--
4155module Streamly.List
42− ( List
56+ (
57+#if __GLASGOW_HASKELL__ >= 800
58+ List (.., Nil, Cons)
59+#else
60+ List (..)
4361 , pattern Nil
4462 , pattern Cons
45− , fromSerial
46− , toSerial
63+#endif
64+ , ZipList (..)
65+ , fromZipList
66+ , toZipList
4767 )
4868where
4969
5070import Control.Arrow (second)
5171import Control.DeepSeq (NFData(..), NFData1(..))
5272import Data.Functor.Identity (Identity, runIdentity)
73+import Data.Semigroup (Semigroup(..))
5374import GHC.Exts (IsList(..), IsString(..))
54−import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
55− readListPrecDefault)
5675
5776import Streamly.Streams.Serial (SerialT)
77+import Streamly.Streams.Zip (ZipSerialM)
78+
5879import qualified Streamly.Streams.Prelude as P
5980import qualified Streamly.Streams.StreamK as K
6081
@@ -63,23 +84,13 @@ import qualified Streamly.Streams.StreamK as K
6384-- using a stream type the programmer needs to specify the Monad otherwise the
6485-- type remains ambiguous.
6586--
66−-- | A better replacement for the standard lists in 'base'.
87+-- | @List a@ is a replacement for @[a]@.
6788--
6889-- @since 0.5.3
69−newtype List a = List { unList :: SerialT Identity a }
70− deriving (Eq, Ord, IsList, NFData, NFData1
71− , Semigroup, Monoid, Functor, Applicative, Monad)
72−
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 };
90+newtype List a = List { toSerial :: SerialT Identity a }
91+ deriving (Show, Read, Eq, Ord, IsList, NFData, NFData1
92+ , Semigroup, Monoid, Functor, Foldable
93+ , Applicative, Traversable, Monad)
8394
8495instance (a ~ Char) => IsString (List a) where
8596 {-# INLINE fromString #-}
@@ -94,34 +105,47 @@ instance (a ~ Char) => IsString (List a) where
94105--
95106-- @since 0.5.3
96107pattern Nil :: List a
97−pattern Nil <- (runIdentity . K.null . unList -> True) where
108+pattern Nil <- (runIdentity . K.null . toSerial -> True) where
98109 Nil = List K.nil
99110
111+infixr 5 `Cons`
112+
100113-- | A list constructor and pattern that deconstructs a 'List' into its head
101114-- and tail. Corresponds to ':' for Haskell lists.
102115--
103116-- @since 0.5.3
104117pattern Cons :: a -> List a -> List a
105118pattern Cons x xs <-
106− (fmap (second List) . runIdentity . K.uncons . unList
119+ (fmap (second List) . runIdentity . K.uncons . toSerial
107120 -> 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 #-}
109124
110125------------------------------------------------------------------------------
111−-- Conversion to and from stream
126+-- ZipList
112127------------------------------------------------------------------------------
113128
114−-- We do not expose the newtype wrapper as that would require the show
115−-- instance to show it which makes show and read more complicated.
129+-- | Just like 'List' except that it has a zipping 'Applicative' instance
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
116140
117−-- | Convert a 'Serial' stream to a 'List'.
141+-- | Convert a 'ZipList' to a regular 'List'
118142--
119143-- @since 0.5.3
120−fromSerial :: SerialT Identity a -> List a
121−fromSerial = List
144+fromZipList :: ZipList a -> List a
145+fromZipList = List . K.adapt . toZipSerial
122146
123−-- | Convert a 'List' to a 'Serial' stream.
147+-- | Convert a regular 'List' to a 'ZipList'
124148--
125149-- @since 0.5.3
126−toSerial :: List a -> SerialT Identity a
127−toSerial = unList
150+toZipList :: List a -> ZipList a
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 { \
4141 put x = lift (put x); \
4242 state k = lift (state k) }
4343
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(..))
5757import GHC.Exts (IsList(..), IsString(..))
5858import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
5959 readListPrecDefault)
60−import Prelude hiding (map, mapM, String)
60+import Prelude hiding (map, mapM)
6161
6262import Streamly.SVar (rstState)
6363import Streamly.Streams.StreamK (IsStream(..), adapt, Stream(..))
@@ -351,66 +351,5 @@ instance Monad m => Monad (WSerialT m) where
351351MONAD_APPLICATIVE_INSTANCE(WSerialT,)
352352MONAD_COMMON_INSTANCES(WSerialT,)
353353
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−
415354LIST_INSTANCES(SerialT)
416355LIST_INSTANCES(WSerialT)
src/Streamly/Streams/Zip.hs+9
@@ -6,6 +6,7 @@
66{-# LANGUAGE GeneralizedNewtypeDeriving#-}
77{-# LANGUAGE InstanceSigs #-}
88{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE TypeFamilies #-}
910{-# LANGUAGE UndecidableInstances #-} -- XXX
1011
1112-- |
@@ -38,7 +39,12 @@ module Streamly.Streams.Zip
3839 )
3940where
4041
42+import Control.DeepSeq (NFData(..), NFData1(..), rnf1)
43+import Data.Functor.Identity (Identity, runIdentity)
4144import Data.Semigroup (Semigroup(..))
45+import GHC.Exts (IsList(..), IsString(..))
46+import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
47+ readListPrecDefault)
4248import Prelude hiding (map, repeat, zipWith)
4349
4450import Streamly.Streams.StreamK (IsStream(..), Stream(..))
@@ -46,6 +52,7 @@ import Streamly.Streams.Async (mkAsync')
4652import Streamly.Streams.Serial (map)
4753import Streamly.SVar (MonadAsync, rstState)
4854
55+import qualified Streamly.Streams.Prelude as P
4956import qualified Streamly.Streams.StreamK as K
5057
5158#include "Instances.hs"
@@ -112,6 +119,8 @@ instance IsStream ZipSerialM where
112119 (|:) :: Monad m => m a -> ZipSerialM m a -> ZipSerialM m a
113120 m |: r = fromStream $ K.consMSerial m (toStream r)
114121
122+LIST_INSTANCES(ZipSerialM)
123+
115124instance Monad m => Functor (ZipSerialM m) where
116125 fmap = map
117126
src/Streamly/String.hs+2−2
@@ -12,8 +12,8 @@
1212-- type and can be used almost as a drop-in replacement, especially when used
1313-- with @OverloadedStrings@ extension, with little differences.
1414--
15−-- See "Streamly.List", <docs/streamly-vs-lists.md> for more details and
16−-- <test/PureStreams.hs> for comprehensive usage examples.
15+-- See "Streamly.List", <src/docs/streamly-vs-lists.md> for more details and
16+-- <src/test/PureStreams.hs> for comprehensive usage examples.
1717--
1818--
1919module Streamly.String
streamly.cabal+26−1
@@ -228,7 +228,7 @@ test-suite test
228228 , exceptions >= 0.8 && < 0.11
229229 default-language: Haskell2010
230230
231−test-suite pure-streams
231+test-suite pure-streams-base
232232 type: exitcode-stdio-1.0
233233 main-is: PureStreams.hs
234234 hs-source-dirs: test
@@ -252,6 +252,31 @@ test-suite pure-streams
252252 , hspec >= 2.0 && < 3
253253 default-language: Haskell2010
254254
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+
255280test-suite properties
256281 type: exitcode-stdio-1.0
257282 main-is: Prop.hs
test/PureStreams.hs+127−20
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE FlexibleInstances #-}
23{-# LANGUAGE MonadComprehensions #-}
34{-# LANGUAGE TypeFamilies #-}
@@ -12,16 +13,33 @@ module Main (main) where
1213import Test.Hspec
1314import qualified GHC.Exts as GHC
1415
15−import Data.Functor.Identity
1616import Streamly
17−import Streamly.List (pattern Cons, pattern Nil)
17+
18+#ifdef USE_STREAMLY_LIST
19+import Data.Functor.Identity
20+import Streamly.List (List(..), pattern Cons, pattern Nil, ZipList(..),
21+ fromZipList, toZipList)
1822import 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
2037
2138main :: IO ()
2239main = hspec $ do
40+#ifdef USE_STREAMLY_LIST
2341 describe "OverloadedLists for 'SerialT Identity' type" $ do
24− it "overloaded lists" $ do
42+ it "Overloaded lists" $ do
2543 ([1..3] :: SerialT Identity Int) `shouldBe` S.fromList [1..3]
2644 GHC.toList ([1..3] :: SerialT Identity Int) `shouldBe` [1..3]
2745
@@ -41,60 +59,149 @@ main = hspec $ do
4159 [(x,y) | x <- [1..2], y <- [1..2]] `shouldBe`
4260 ([(1,1), (1,2), (2,1), (2,2)] :: SerialT Identity (Int, Int))
4361
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+
4470 describe "OverloadedLists for List type" $ do
4571 it "overloaded lists" $ do
46− [1..3 :: Int] `shouldBe` S.fromSerial (S.fromList [1..3])
47− GHC.toList ([1..3] :: S.List Int) `shouldBe` [1..3]
72+ ([1..3] :: List Int) `shouldBe` GHC.fromList [1..3]
73+ GHC.toList ([1..3] :: List Int) `shouldBe` [1..3]
4874
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` []
5079 ('x' `Cons` Nil) `shouldBe` ['x']
5180 (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"
5291
53− it "pattern match on non-empty list" $
5492 case [1..10 :: Int] of
5593 Cons x xs -> do
5694 x `shouldBe` 1
5795 xs `shouldBe` [2..10]
5896 _ -> expectationFailure "not reached"
5997
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+
60105 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
62112
63113 it "Read instance" $ do
64− (read "fromList [1,2,3]" :: S.List Int) `shouldBe` [1..3]
114+ (read
115+#ifdef USE_STREAMLY_LIST
116+ "List {toSerial = fromList [1,2,3]}"
117+#else
118+ "[1,2,3]"
119+#endif
120+ :: List Int) `shouldBe` [1..3]
65121
66122 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
68124
69125 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
71127
72128 it "Monad comprehension" $ do
73129 [(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))
75131
76− describe "OverloadedStrings for 'SerialT Identity' type" $ do
77− it "overloaded strings" $ do
78− ("hello" :: SerialT Identity Char) `shouldBe` S.fromList "hello"
132+ it "Foldable (sum)" $ sum ([1..3] :: List Int) `shouldBe` 6
79133
80134 describe "OverloadedStrings for List type" $ do
81135 it "overloaded strings" $ do
82− "hello" `shouldBe` S.fromSerial (S.fromList "hello")
136+ ("hello" :: List Char) `shouldBe` GHC.fromList "hello"
83137
84− it "pattern match on empty string" $
138+ it "pattern matches" $ do
85139 case "" of
86140 Nil -> return ()
87141 _ -> expectationFailure "not reached"
88142
89− it "pattern matches on singleton string" $
90143 case "a" of
91144 Cons x Nil -> x `shouldBe` 'a'
92145 _ -> expectationFailure "not reached"
93146
94− it "pattern matches on non-empty string" $
95147 case "hello" <> "world" of
96148 Cons x1 (Cons x2 xs) -> do
97149 x1 `shouldBe` 'h'
98150 x2 `shouldBe` 'e'
99151 xs `shouldBe` "lloworld"
100152 _ -> 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