@@ -0,0 +1,523 @@
4+-- Module : Streamly.Enumeration
5+-- Copyright : (c) 2018 Harendra Kumar
8+-- Maintainer : harendra.kumar@gmail.com
9+-- Stability : experimental
12+-- The functions defined in this module should be rarely needed for direct use,
13+-- try to use the operations from the 'Enumerable' type class
16+-- This module provides an 'Enumerable' type class to enumerate 'Enum' types
17+-- into a stream. The operations in this type class correspond to similar
18+-- perations in the 'Enum' type class, the only difference is that they produce
19+-- a stream instead of a list. These operations cannot be defined generically
20+-- based on the 'Enum' type class. We provide instances for commonly used
21+-- types. If instances for other types are needed convenience functions defined
22+-- in this module can be used to define them. Alternatively, these functions
23+-- can be used directly.
25+module Streamly.Enumeration
30+ -- ** Enumerating 'Enum' Types
31+ , enumerateFromToSmall
32+ , enumerateFromThenToSmall
33+ , enumerateFromThenSmallBounded
34+ , enumerateFromBounded
36+ -- ** Enumerating 'Bounded' 'Integral' Types
37+ , enumerateFromIntegral
38+ , enumerateFromThenIntegral
40+ -- ** Enumerating unbounded 'Integral' Types
41+ , enumerateFromToIntegral
42+ , enumerateFromThenToIntegral
43+ , enumerateFromStepIntegral
45+ -- ** Enumerating unbounded 'Fractional' Types
46+ , enumerateFromFractional
47+ , enumerateFromToFractional
48+ , enumerateFromThenFractional
49+ , enumerateFromThenToFractional
51+ -- ** Enumerating unbounded 'Num' Types
52+ , enumerateFromStepNum
60+import Numeric.Natural
61+import Data.Functor.Identity (Identity(..))
63+import Streamly.Streams.StreamD (fromStreamD)
64+import Streamly.Streams.StreamK (IsStream(..))
66+import qualified Streamly.Streams.StreamD as D
67+import qualified Streamly.Streams.Serial as Serial
69+-------------------------------------------------------------------------------
70+-- Enumeration of Integral types
71+-------------------------------------------------------------------------------
73+-- | @enumerateFromStepIntegral from step@ generates an infinite stream whose
74+-- first element is @from@ and the successive elements are in increments of
75+-- @step@. This does not check for overflow or underflow if the 'Integral' type
79+-- > S.toList $ S.take 4 $ S.enumerateFromStepIntegral 0 2
81+-- > S.toList $ S.take 3 $ S.enumerateFromStepIntegral 0 (-2)
86+{-# INLINE enumerateFromStepIntegral #-}
87+enumerateFromStepIntegral
88+ :: (IsStream t, Monad m, Integral a)
90+enumerateFromStepIntegral from stride =
91+ fromStreamD $ D.enumerateFromStepIntegral from stride
93+-- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a
94+-- stream whose first element is @from@ and the successive elements are in
95+-- increments of @1@. The stream is bounded by the size of the 'Integral' type.
98+-- > S.toList $ S.take 4 $ S.enumerateFromIntegral (0 :: Int)
103+{-# INLINE enumerateFromIntegral #-}
104+enumerateFromIntegral
105+ :: (IsStream t, Monad m, Integral a, Bounded a)
107+enumerateFromIntegral from = fromStreamD $ D.enumerateFromIntegral from
109+-- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from
110+-- then@ generates a stream whose first element is @from@, the second element
111+-- is @then@ and the successive elements are in increments of @then - from@.
112+-- The stream is bounded by the size of the 'Integral' type.
115+-- > S.toList $ S.take 4 $ S.enumerateFromThenIntegral (0 :: Int) 2
117+-- > S.toList $ S.take 4 $ S.enumerateFromThenIntegral (0 :: Int) (-2)
122+{-# INLINE enumerateFromThenIntegral #-}
123+enumerateFromThenIntegral
124+ :: (IsStream t, Monad m, Integral a, Bounded a)
126+enumerateFromThenIntegral from next =
127+ fromStreamD $ D.enumerateFromThenIntegral from next
129+-- | Enumerate an 'Integral' type up to a given limit.
130+-- @enumerateFromToIntegral from to@ generates a finite stream whose first
131+-- element is @from@ and successive elements are in increments of @1@ up to
135+-- > S.toList $ S.enumerateFromToIntegral 0 4
140+{-# INLINE enumerateFromToIntegral #-}
141+enumerateFromToIntegral :: (IsStream t, Monad m, Integral a) => a -> a -> t m a
142+enumerateFromToIntegral from to =
143+ fromStreamD $ D.enumerateFromToIntegral from to
145+-- | Enumerate an 'Integral' type in steps up to a given limit.
146+-- @enumerateFromThenToIntegral from then to@ generates a finite stream whose
147+-- first element is @from@, the second element is @then@ and the successive
148+-- elements are in increments of @then - from@ up to @to@.
151+-- > S.toList $ S.enumerateFromThenToIntegral 0 2 6
153+-- > S.toList $ S.enumerateFromThenToIntegral 0 (-2) (-6)
158+{-# INLINE enumerateFromThenToIntegral #-}
159+enumerateFromThenToIntegral
160+ :: (IsStream t, Monad m, Integral a)
161+ => a -> a -> a -> t m a
162+enumerateFromThenToIntegral from next to =
163+ fromStreamD $ D.enumerateFromThenToIntegral from next to
165+-------------------------------------------------------------------------------
166+-- Enumeration of Num types
167+-------------------------------------------------------------------------------
169+-- | @enumerateFromStepNum from step@ generates an infinite stream whose first
170+-- element is @from@ and the successive elements are in increments of @step@.
171+-- This is numerically stable but does not check for overflow or underflow for
172+-- bounded types. Note, for 'Integral' types 'enumerateFromStepIntegral' is
177+-- > S.toList $ S.take 4 $ S.enumerateFromStepNum 0.1 2
179+-- > S.toList $ S.take 3 $ S.enumerateFromStepNum 0.1 (-2)
180+-- [0.1,-1.9,-3.9,-5.9]
184+{-# INLINE enumerateFromStepNum #-}
185+enumerateFromStepNum :: (IsStream t, Monad m, Num a) => a -> a -> t m a
186+enumerateFromStepNum from stride =
187+ fromStreamD $ D.enumerateFromStepNum from stride
189+-------------------------------------------------------------------------------
190+-- Enumeration of Fractional types
191+-------------------------------------------------------------------------------
193+-- Even though the underlying implementation of enumerateFromFractional and
194+-- enumerateFromThenFractional works for any 'Num' we have restricted these to
195+-- 'Fractional' because these do not perform any bounds check, in contrast to
196+-- integral versions and are therefore not equivalent substitutes for those.
198+-- | Numerically stable enumeration from a 'Fractional' number in steps of size
199+-- @1@. @enumerateFromFractional from@ generates a stream whose first element
200+-- is @from@ and the successive elements are in increments of @1@. No overflow
201+-- or underflow checks are performed.
203+-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
206+-- > S.toList $ S.take 4 $ S.enumerateFromFractional 1.1
212+{-# INLINE enumerateFromFractional #-}
213+enumerateFromFractional :: (IsStream t, Monad m, Fractional a) => a -> t m a
214+enumerateFromFractional from = fromStreamD $ D.numFrom from
216+-- | Numerically stable enumeration from a 'Fractional' number in steps.
217+-- @enumerateFromThenFractional from then@ generates a stream whose first
218+-- element is @from@, the second element is @then@ and the successive elements
219+-- are in increments of @then - from@. No overflow or underflow checks are
222+-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
226+-- > S.toList $ S.take 4 $ S.enumerateFromThenFractional 1.1 2.1
228+-- > S.toList $ S.take 4 $ S.enumerateFromThenFractional 1.1 (-2.1)
229+-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
233+{-# INLINE enumerateFromThenFractional #-}
234+enumerateFromThenFractional
235+ :: (IsStream t, Monad m, Fractional a)
237+enumerateFromThenFractional from next = fromStreamD $ D.numFromThen from next
239+-- | Numerically stable enumeration from a 'Fractional' number to a given
240+-- limit. @enumerateFromToFractional from to@ generates a finite stream whose
241+-- first element is @from@ and successive elements are in increments of @1@ up
244+-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
248+-- > S.toList $ S.enumerateFromToFractional 1.1 4
250+-- > S.toList $ S.enumerateFromToFractional 1.1 4.6
251+-- [1.1,2.1,3.1,4.1,5.1]
254+-- Notice that the last element is equal to the specified @to@ value after
255+-- rounding to the nearest integer.
258+{-# INLINE enumerateFromToFractional #-}
259+enumerateFromToFractional
260+ :: (IsStream t, Monad m, Fractional a, Ord a)
262+enumerateFromToFractional from to =
263+ fromStreamD $ D.enumerateFromToFractional from to
265+-- | Numerically stable enumeration from a 'Fractional' number in steps up to a
266+-- given limit. @enumerateFromThenToFractional from then to@ generates a
267+-- finite stream whose first element is @from@, the second element is @then@
268+-- and the successive elements are in increments of @then - from@ up to @to@.
270+-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
274+-- > S.toList $ S.enumerateFromThenToFractional 0.1 2 6
275+-- [0.1,2.0,3.9,5.799999999999999]
276+-- > S.toList $ S.enumerateFromThenToFractional 0.1 (-2) (-6)
277+-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
282+{-# INLINE enumerateFromThenToFractional #-}
283+enumerateFromThenToFractional
284+ :: (IsStream t, Monad m, Fractional a, Ord a)
285+ => a -> a -> a -> t m a
286+enumerateFromThenToFractional from next to =
287+ fromStreamD $ D.enumerateFromThenToFractional from next to
289+-------------------------------------------------------------------------------
290+-- Enumeration of Enum types
291+-------------------------------------------------------------------------------
293+-- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'. This function
294+-- does not check the bounds for 'Bounded' types, it assumes that the @to@
295+-- parameter is constrained by the type to be within the range representable by
297+{-# INLINE enumerateFromToSmall #-}
298+enumerateFromToSmall :: (IsStream t, Monad m, Enum a) => a -> a -> t m a
299+enumerateFromToSmall from to = Serial.map toEnum $
300+ enumerateFromToIntegral (fromEnum from) (fromEnum to)
302+-- | 'enumerateFrom' for 'Bounded' 'Enum' types. This is defined in terms of
303+-- 'enumerateFromTo', therefore it will work even for types larger than 'Int'
304+-- depending on 'enumerateFromTo'.
305+{-# INLINE enumerateFromBounded #-}
306+enumerateFromBounded :: (IsStream t, Monad m, Enumerable a, Bounded a)
308+enumerateFromBounded from = enumerateFromTo from maxBound
310+-- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'. This
311+-- function does not check the bounds for 'Bounded' types, it assumes that the
312+-- @to@ parameter is constrained by the type to be within the range
313+-- representable by the type.
314+{-# INLINE enumerateFromThenToSmall #-}
315+enumerateFromThenToSmall :: (IsStream t, Monad m, Enum a)
316+ => a -> a -> a -> t m a
317+enumerateFromThenToSmall from next to = Serial.map toEnum $
318+ enumerateFromThenToIntegral (fromEnum from) (fromEnum next) (fromEnum to)
320+-- | 'enumerateFromThen' for 'Bounded' 'Enum' types not larger than 'Int'. For
321+-- types smaller than 'Int' we know it is bounded though it may not have a
322+-- 'Bounded' instance. It is not necessary to require 'Bounded' instance for
323+-- all small types, we require it anyway because it is safe that way.
324+{-# INLINE enumerateFromThenSmallBounded #-}
325+enumerateFromThenSmallBounded :: (IsStream t, Monad m, Enumerable a, Bounded a)
327+enumerateFromThenSmallBounded from next =
328+ case fromEnum next >= fromEnum from of
329+ True -> enumerateFromThenTo from next maxBound
330+ False -> enumerateFromThenTo from next minBound
332+-------------------------------------------------------------------------------
333+-- Enumerable type class
334+-------------------------------------------------------------------------------
336+-- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream
337+-- enumerations like this:
339+-- {-# RULES "fromList enumFrom" [1]
340+-- forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}
342+-- But this does not work because enumFrom is a class method and GHC rewrites
343+-- it quickly, so we do not get a chance to have our rule fired.
345+-- | Types that can be enumerated as a stream. The operations in this type
346+-- class are equivalent to those in the 'Enum' type class, except that these
347+-- generate a stream instead of a list. Use the functions in
348+-- "Streamly.Enumeration" module to define new instances.
351+class Enum a => Enumerable a where
352+ -- | @enumerateFrom from@ generates a stream starting with the element
353+ -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
354+ -- generating an infinite stream when the type is not 'Bounded'.
357+ -- > S.toList $ S.take 4 $ S.enumerateFrom (0 :: Int)
361+ -- For 'Fractional' types, enumeration is numerically stable. However, no
362+ -- overflow or underflow checks are performed.
365+ -- > S.toList $ S.take 4 $ S.enumerateFrom 1.1
366+ -- [1.1,2.1,3.1,4.1]
369+ enumerateFrom :: (IsStream t, Monad m) => a -> t m a
371+ -- | Generate a finite stream starting with the element @from@, enumerating
372+ -- the type up to the value @to@. If @to@ is smaller than @from@ then an
373+ -- empty stream is returned.
376+ -- > S.toList $ S.enumerateFromTo 0 4
380+ -- For 'Fractional' types, the last element is equal to the specified @to@
381+ -- value after rounding to the nearest integral value.
384+ -- > S.toList $ S.enumerateFromTo 1.1 4
385+ -- [1.1,2.1,3.1,4.1]
386+ -- > S.toList $ S.enumerateFromTo 1.1 4.6
387+ -- [1.1,2.1,3.1,4.1,5.1]
390+ enumerateFromTo :: (IsStream t, Monad m) => a -> a -> t m a
392+ -- | @enumerateFromThen from then@ generates a stream whose first element
393+ -- is @from@, the second element is @then@ and the successive elements are
394+ -- in increments of @then - from@. Enumeration can occur downwards or
395+ -- upwards depending on whether @then@ comes before or after @from@. For
396+ -- 'Bounded' types the stream ends when 'maxBound' is reached, for
397+ -- unbounded types it keeps enumerating infinitely.
400+ -- > S.toList $ S.take 4 $ S.enumerateFromThen 0 2
402+ -- > S.toList $ S.take 4 $ S.enumerateFromThen 0 (-2)
405+ enumerateFromThen :: (IsStream t, Monad m) => a -> a -> t m a
407+ -- | @enumerateFromThenTo from then to@ generates a finite stream whose
408+ -- first element is @from@, the second element is @then@ and the successive
409+ -- elements are in increments of @then - from@ up to @to@. Enumeration can
410+ -- occur downwards or upwards depending on whether @then@ comes before or
414+ -- > S.toList $ S.enumerateFromThenTo 0 2 6
416+ -- > S.toList $ S.enumerateFromThenTo 0 (-2) (-6)
419+ enumerateFromThenTo :: (IsStream t, Monad m) => a -> a -> a -> t m a
421+-- For Enum types smaller than or equal to Int size.
422+#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE) \
423+instance Enumerable SMALL_TYPE where { \
424+ {-# INLINE enumerateFrom #-}; \
425+ enumerateFrom = enumerateFromBounded; \
426+ {-# INLINE enumerateFromThen #-}; \
427+ enumerateFromThen = enumerateFromThenSmallBounded; \
428+ {-# INLINE enumerateFromTo #-}; \
429+ enumerateFromTo = enumerateFromToSmall; \
430+ {-# INLINE enumerateFromThenTo #-}; \
431+ enumerateFromThenTo = enumerateFromThenToSmall }
434+ENUMERABLE_BOUNDED_SMALL(())
435+ENUMERABLE_BOUNDED_SMALL(Bool)
436+ENUMERABLE_BOUNDED_SMALL(Ordering)
437+ENUMERABLE_BOUNDED_SMALL(Char)
439+-- For bounded Integral Enum types, may be larger than Int.
440+#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE) \
441+instance Enumerable INTEGRAL_TYPE where { \
442+ {-# INLINE enumerateFrom #-}; \
443+ enumerateFrom = enumerateFromIntegral; \
444+ {-# INLINE enumerateFromThen #-}; \
445+ enumerateFromThen = enumerateFromThenIntegral; \
446+ {-# INLINE enumerateFromTo #-}; \
447+ enumerateFromTo = enumerateFromToIntegral; \
448+ {-# INLINE enumerateFromThenTo #-}; \
449+ enumerateFromThenTo = enumerateFromThenToIntegral }
451+ENUMERABLE_BOUNDED_INTEGRAL(Int)
452+ENUMERABLE_BOUNDED_INTEGRAL(Int8)
453+ENUMERABLE_BOUNDED_INTEGRAL(Int16)
454+ENUMERABLE_BOUNDED_INTEGRAL(Int32)
455+ENUMERABLE_BOUNDED_INTEGRAL(Int64)
456+ENUMERABLE_BOUNDED_INTEGRAL(Word)
457+ENUMERABLE_BOUNDED_INTEGRAL(Word8)
458+ENUMERABLE_BOUNDED_INTEGRAL(Word16)
459+ENUMERABLE_BOUNDED_INTEGRAL(Word32)
460+ENUMERABLE_BOUNDED_INTEGRAL(Word64)
462+-- For unbounded Integral Enum types.
463+#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE) \
464+instance Enumerable INTEGRAL_TYPE where { \
465+ {-# INLINE enumerateFrom #-}; \
466+ enumerateFrom from = enumerateFromStepIntegral from 1; \
467+ {-# INLINE enumerateFromThen #-}; \
468+ enumerateFromThen from next = \
469+ enumerateFromStepIntegral from (next - from); \
470+ {-# INLINE enumerateFromTo #-}; \
471+ enumerateFromTo = enumerateFromToIntegral; \
472+ {-# INLINE enumerateFromThenTo #-}; \
473+ enumerateFromThenTo = enumerateFromThenToIntegral }
475+ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)
476+ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)
478+#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT) \
479+instance (CONSTRAINT) => Enumerable (FRACTIONAL_TYPE) where { \
480+ {-# INLINE enumerateFrom #-}; \
481+ enumerateFrom = enumerateFromFractional; \
482+ {-# INLINE enumerateFromThen #-}; \
483+ enumerateFromThen = enumerateFromThenFractional; \
484+ {-# INLINE enumerateFromTo #-}; \
485+ enumerateFromTo = enumerateFromToFractional; \
486+ {-# INLINE enumerateFromThenTo #-}; \
487+ enumerateFromThenTo = enumerateFromThenToFractional }
489+ENUMERABLE_FRACTIONAL(Float,)
490+ENUMERABLE_FRACTIONAL(Double,)
491+ENUMERABLE_FRACTIONAL(Fixed a,HasResolution a)
492+ENUMERABLE_FRACTIONAL(Ratio a,Integral a)
494+#if __GLASGOW_HASKELL__ >= 800
495+instance Enumerable a => Enumerable (Identity a) where
496+ {-# INLINE enumerateFrom #-}
497+ enumerateFrom (Identity from) = Serial.map Identity $
499+ {-# INLINE enumerateFromThen #-}
500+ enumerateFromThen (Identity from) (Identity next) = Serial.map Identity $
501+ enumerateFromThen from next
502+ {-# INLINE enumerateFromTo #-}
503+ enumerateFromTo (Identity from) (Identity to) = Serial.map Identity $
504+ enumerateFromTo from to
505+ {-# INLINE enumerateFromThenTo #-}
506+ enumerateFromThenTo (Identity from) (Identity next) (Identity to) =
507+ Serial.map Identity $ enumerateFromThenTo from next to
511+instance Enumerable a => Enumerable (Last a)
512+instance Enumerable a => Enumerable (First a)
513+instance Enumerable a => Enumerable (Max a)
514+instance Enumerable a => Enumerable (Min a)
515+instance Enumerable a => Enumerable (Const a b)
516+instance Enumerable (f a) => Enumerable (Alt f a)
517+instance Enumerable (f a) => Enumerable (Ap f a)
520+-- | Enumerate a finite ('Bounded') data type from its 'minBound' to 'maxBound'
521+{-# INLINE enumerate #-}
522+enumerate :: (IsStream t, Monad m, Bounded a, Enumerable a) => t m a
523+enumerate = enumerateFrom minBound