on 3ce574a help sign in
188 lines 6.6 kB history raw
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE PatternSynonyms #-}
5{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE UndecidableInstances #-} -- XXX
7{-# LANGUAGE ViewPatterns #-}
9-- |
10-- Module : Streamly.List
11-- Copyright : (c) 2018 Composewell Technologies
12--
13-- License : BSD3
14-- Maintainer : harendra.kumar@gmail.com
15-- Stability : experimental
16-- Portability : GHC
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
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. There would be a slight difference in the 'Show' and 'Read'
26-- strings of streamly list as compared to regular lists.
27--
28-- Conversion to stream types is free, any stream combinator can be used on
29-- lists by converting them to streams. However, for convenience, this module
30-- provides combinators that work directly on the 'List' type.
31--
32--
33-- @
34-- List $ S.map (+ 1) $ toSerial (1 \`Cons\` Nil)
35-- @
36--
37-- To convert a 'List' to regular lists, you can use any of the following:
38--
39-- * @toList . toSerial@ and @toSerial . fromList@
40-- * 'Data.Foldable.toList' from "Data.Foldable"
41-- * 'GHC.Exts.toList' and 'GHC.Exts.fromList' from 'IsList' in "GHC.Exts"
42--
43-- If you have made use of 'Nil' and 'Cons' constructors in the code and you
44-- want to replace streamly lists with standard lists, all you need to do is
45-- import these definitions:
46--
47-- @
48-- type List = []
49-- pattern Nil <- [] where Nil = []
50-- pattern Cons x xs = x : xs
51-- infixr 5 `Cons`
52-- {-\# COMPLETE Cons, Nil #-}
53-- @
54--
55-- See <src/docs/streamly-vs-lists.md> for more details and
56-- <src/test/PureStreams.hs> for comprehensive usage examples.
57--
58module Streamly.List
59 (
60#if __GLASGOW_HASKELL__ >= 800
61 List (.., Nil, Cons)
62#else
63 List (..)
64 , pattern Nil
65 , pattern Cons
66#endif
67 -- XXX we may want to use rebindable syntax for variants instead of using
68 -- different types (applicative do and apWith).
69 , ZipList (..)
70 , fromZipList
71 , toZipList
72 )
73where
75import Control.Arrow (second)
76import Control.DeepSeq (NFData(..), NFData1(..))
77import Data.Functor.Identity (Identity, runIdentity)
78import Data.Semigroup (Semigroup(..))
79import GHC.Exts (IsList(..), IsString(..))
81import Streamly.Streams.Serial (SerialT)
82import Streamly.Streams.Zip (ZipSerialM)
84import qualified Streamly.Streams.Prelude as P
85import qualified Streamly.Streams.StreamK as K
87-- We implement list as a newtype instead of a type synonym to make type
88-- inference easier when using -XOverloadedLists and -XOverloadedStrings. When
89-- using a stream type the programmer needs to specify the Monad otherwise the
90-- type remains ambiguous.
91--
92-- XXX once we separate consM from IsStream or remove the MonadIO and
93-- MonadBaseControlIO dependency from it, then we can make this an instance of
94-- IsStream and use the regular polymorphic functions on Lists as well. Once
95-- that happens we can change the Show and Read instances as well to use "1 >:
96-- 2 >: nil" etc. or should we use a separate constructor indicating the "List"
97-- type ":>" for better inference?
98--
99-- | @List a@ is a replacement for @[a]@.
100--
101-- @since 0.6.0
102newtype List a = List { toSerial :: SerialT Identity a }
103 deriving (Show, Read, Eq, Ord, NFData, NFData1
104 , Semigroup, Monoid, Functor, Foldable
105 , Applicative, Traversable, Monad)
107instance (a ~ Char) => IsString (List a) where
108 {-# INLINE fromString #-}
109 fromString = List . P.fromList
111-- GHC versions 8.0 and below cannot derive IsList
112instance IsList (List a) where
113 type (Item (List a)) = a
114 {-# INLINE fromList #-}
115 fromList = List . P.fromList
116 {-# INLINE toList #-}
117 toList = runIdentity . P.toList . toSerial
119------------------------------------------------------------------------------
120-- Patterns
121------------------------------------------------------------------------------
123-- Note: When using the OverloadedLists extension we should be able to pattern
124-- match using the regular list contructors. OverloadedLists uses 'toList' to
125-- perform the pattern match, it should not be too bad as it works lazily in
126-- the Identity monad. We need these patterns only when not using that
127-- extension.
128--
129-- | An empty list constructor and pattern that matches an empty 'List'.
130-- Corresponds to '[]' for Haskell lists.
131--
132-- @since 0.6.0
133pattern Nil :: List a
134pattern Nil <- (runIdentity . K.null . toSerial -> True) where
135 Nil = List K.nil
137infixr 5 `Cons`
139-- | A list constructor and pattern that deconstructs a 'List' into its head
140-- and tail. Corresponds to ':' for Haskell lists.
141--
142-- @since 0.6.0
143pattern Cons :: a -> List a -> List a
144pattern Cons x xs <-
145 (fmap (second List) . runIdentity . K.uncons . toSerial
146 -> Just (x, xs)) where
147 Cons x xs = List $ K.cons x (toSerial xs)
149#if __GLASGOW_HASKELL__ >= 802
150{-# COMPLETE Nil, Cons #-}
151#endif
153------------------------------------------------------------------------------
154-- ZipList
155------------------------------------------------------------------------------
157-- | Just like 'List' except that it has a zipping 'Applicative' instance
158-- and no 'Monad' instance.
159--
160-- @since 0.6.0
161newtype ZipList a = ZipList { toZipSerial :: ZipSerialM Identity a }
162 deriving (Show, Read, Eq, Ord, NFData, NFData1
163 , Semigroup, Monoid, Functor, Foldable
164 , Applicative, Traversable)
166instance (a ~ Char) => IsString (ZipList a) where
167 {-# INLINE fromString #-}
168 fromString = ZipList . P.fromList
170-- GHC versions 8.0 and below cannot derive IsList
171instance IsList (ZipList a) where
172 type (Item (ZipList a)) = a
173 {-# INLINE fromList #-}
174 fromList = ZipList . P.fromList
175 {-# INLINE toList #-}
176 toList = runIdentity . P.toList . toZipSerial
178-- | Convert a 'ZipList' to a regular 'List'
179--
180-- @since 0.6.0
181fromZipList :: ZipList a -> List a
182fromZipList = List . K.adapt . toZipSerial
184-- | Convert a regular 'List' to a 'ZipList'
185--
186-- @since 0.6.0
187toZipList :: List a -> ZipList a
188toZipList = ZipList . K.adapt . toSerial