on da36a01 help sign in
166 lines 5.8 kB history raw
1{-# LANGUAGE UndecidableInstances #-}
3-- |
4-- Module : Streamly.Internal.Data.List
5-- Copyright : (c) 2018 Composewell Technologies
6--
7-- License : BSD3
8-- Maintainer : streamly@composewell.com
9-- Stability : pre-release
10-- Portability : GHC
11--
12-- Lists are just a special case of monadic streams. The stream type @Stream
13-- Identity a@ can be used as a replacement for @[a]@. The 'List' type in this
14-- module is just a newtype wrapper around @Stream Identity@ for better type
15-- inference when using the 'OverloadedLists' GHC extension. @List a@ provides
16-- better performance compared to @[a]@. Standard list, string and list
17-- comprehension syntax can be used with the 'List' type by enabling
18-- 'OverloadedLists', 'OverloadedStrings' and 'MonadComprehensions' GHC
19-- extensions. There would be a slight difference in the 'Show' and 'Read'
20-- strings of streamly list as compared to regular lists.
21--
22-- Conversion to stream types is free, any stream combinator can be used on
23-- lists by converting them to streams. However, for convenience, this module
24-- provides combinators that work directly on the 'List' type.
25--
26--
27-- @
28-- List $ S.map (+ 1) $ toStream (1 \`Cons\` Nil)
29-- @
30--
31-- To convert a 'List' to regular lists, you can use any of the following:
32--
33-- * @toList . toStream@ and @toStream . fromList@
34-- * 'Data.Foldable.toList' from "Data.Foldable"
35-- * 'GHC.Exts.toList' and 'GHC.Exts.fromList' from 'IsList' in "GHC.Exts"
36--
37-- If you have made use of 'Nil' and 'Cons' constructors in the code and you
38-- want to replace streamly lists with standard lists, all you need to do is
39-- import these definitions:
40--
41-- @
42-- type List = []
43-- pattern Nil <- [] where Nil = []
44-- pattern Cons x xs = x : xs
45-- infixr 5 `Cons`
46-- {-\# COMPLETE Cons, Nil #-}
47-- @
48--
49-- See <src/docs/streamly-vs-lists.md> for more details and
50-- <src/test/PureStreams.hs> for comprehensive usage examples.
51--
52module Streamly.Internal.Data.List
53 (
54 List (.., Nil, Cons)
56 -- XXX we may want to use rebindable syntax for variants instead of using
57 -- different types (applicative do and apWith).
58 , ZipList (..)
59 , fromZipList
60 , toZipList
61 )
62where
64import Control.Arrow (second)
65import Control.DeepSeq (NFData(..))
66import Control.DeepSeq (NFData1(..))
67import Data.Functor.Identity (Identity, runIdentity)
68import GHC.Exts (IsList(..), IsString(..))
69import Streamly.Internal.Data.Stream.Type (Stream)
70import Streamly.Internal.Data.Stream.Zip (ZipStream(..))
72import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
73import qualified Streamly.Internal.Data.Stream.Type as Stream
75-- We implement list as a newtype instead of a type synonym to make type
76-- inference easier when using -XOverloadedLists and -XOverloadedStrings. When
77-- using a stream type the programmer needs to specify the Monad otherwise the
78-- type remains ambiguous.
79--
80-- XXX once we separate consM from IsStream or remove the MonadIO and
81-- MonadBaseControlIO dependency from it, then we can make this an instance of
82-- IsStream and use the regular polymorphic functions on Lists as well. Once
83-- that happens we can change the Show and Read instances as well to use "1 >:
84-- 2 >: nil" etc. or should we use a separate constructor indicating the "List"
85-- type ":>" for better inference?
86--
87-- | @List a@ is a replacement for @[a]@.
88--
89-- /Pre-release/
90newtype List a = List { toStream :: Stream Identity a }
91 deriving
92 ( Show, Read, Eq, Ord, NFData , NFData1
93 , Semigroup, Monoid, Functor, Foldable
94 , Applicative, Traversable, Monad, IsList)
96instance (a ~ Char) => IsString (List a) where
97 {-# INLINE fromString #-}
98 fromString = List . fromList
100------------------------------------------------------------------------------
101-- Patterns
102------------------------------------------------------------------------------
104-- Note: When using the OverloadedLists extension we should be able to pattern
105-- match using the regular list contructors. OverloadedLists uses 'toList' to
106-- perform the pattern match, it should not be too bad as it works lazily in
107-- the Identity monad. We need these patterns only when not using that
108-- extension.
109--
110-- | An empty list constructor and pattern that matches an empty 'List'.
111-- Corresponds to '[]' for Haskell lists.
112--
113-- @since 0.6.0
114pattern Nil :: List a
115pattern Nil <- (runIdentity . K.null . Stream.toStreamK . toStream -> True) where
116 Nil = List $ Stream.fromStreamK K.nil
118infixr 5 `Cons`
120-- | A list constructor and pattern that deconstructs a 'List' into its head
121-- and tail. Corresponds to ':' for Haskell lists.
122--
123-- @since 0.6.0
124pattern Cons :: a -> List a -> List a
125pattern Cons x xs <-
126 (fmap (second (List . Stream.fromStreamK))
127 . runIdentity . K.uncons . Stream.toStreamK . toStream
128 -> Just (x, xs)
129 )
131 where
133 Cons x xs = List $ Stream.cons x (toStream xs)
135{-# COMPLETE Nil, Cons #-}
137------------------------------------------------------------------------------
138-- ZipList
139------------------------------------------------------------------------------
141-- | Just like 'List' except that it has a zipping 'Applicative' instance
142-- and no 'Monad' instance.
143--
144-- @since 0.6.0
145newtype ZipList a = ZipList { toZipStream :: ZipStream Identity a }
146 deriving
147 ( Show, Read, Eq, Ord, NFData , NFData1
148 , Semigroup, Monoid, Functor, Foldable
149 , Applicative, Traversable, IsList
150 )
152instance (a ~ Char) => IsString (ZipList a) where
153 {-# INLINE fromString #-}
154 fromString = ZipList . fromList
156-- | Convert a 'ZipList' to a regular 'List'
157--
158-- @since 0.6.0
159fromZipList :: ZipList a -> List a
160fromZipList (ZipList zs) = List $ getZipStream zs
162-- | Convert a regular 'List' to a 'ZipList'
163--
164-- @since 0.6.0
165toZipList :: List a -> ZipList a
166toZipList = ZipList . ZipStream . toStream