typed-streams-0.1.0.0: src/Data/Stream/Typed.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
{-|
The motivation of this library is at least partly demostrated by the following problem with lists:
Consider the following code (which is taken from Tests.hs from this package btw):
> f :: Int -> Int
> f x = x*(x .&. 3)
>
> g :: Int -> Int
> g x = x*(x .&. 7)
@f@ and @g@ are just silly example functions, which are effectively:
> f x = x * (x mod 8)
> g x = x * (x mod 16)
Now lets say we want to take some \"list\", apply f to it, apply g to it,
append both these together, and fold them. A straightforward way would be this:
> sumG :: (Functor t, Foldable t, Semigroup (t Int)) => t Int -> Int
> sumG x = foldl' (+) 0 ((fmap f x) <> (fmap g x))
For comparison sake, lets write a hand written version of this function:
> fast :: Int -> Int
> fast n = go g (go f 0 1 n) 1 n where
> go :: (Int -> Int) -> Int -> Int -> Int -> Int
> go f = go' where
> go' :: Int -> Int -> Int -> Int
> go' acc s i = if i == 0 then acc else let next_acc = acc + f s in next_acc `seq` go' next_acc (s + 1) (i - 1)
What you will probably find is, at least with GHC 8.0.2 which I've tested it with:
> sumG [1..n]
is about ten times slower than
> fast n
Even though they should be doing the same thing.
But, using this stream library, and 'Data.Generic.Enum.EnumFromTo' from another package, you can write:
> sumG (enumFromTo 1 n)
And this runs almost as fast as the handwritten code.
Now you may be able to get this speed out of ordinary lists with some fancy rewrite rules
(and indeed this Stream library does have a few fancy rewrite rules itself) there more
theortical advantages that Data.Stream.Stream' could have over lists.
Unlike ordinary lists, streams do not store the data directly. They just store a way to generate the data.
What does this mean?
At the moment, the main way to process a stream is to fold over it. You can't really deconstruct it
step by step. But generally folds give you enough power to process a list.
Also, if you fold over a stream twice, you'll have to recalculate it. This is a good and bad thing,
It can be bad because you have to recalculate, but it's good because you won't use up memory.
For many lists used in practice, they're simple enough to regenerate instead of storing, and it prevents
huge heap usage from code like this:
> average x = (foldl' (+) 0 x) / (length x)
There's other advantages to this approach. Firstly, appending streams is always a constant time operation.
Always. Even if the first stream is infinite. All appending streams does is generate a new "stream" which has
the two appended streams as data items.
Actually, our stream data type is more sophisticated than this. A 'Stream' is a type of two variables, the second
is the element type as usual, but the first is the \"Length\". Streams can be the following lengths:
* Infinite
* Unknown
* RunTime
* CompileTime
* Empty
Infinite streams are well, infinte, not much to say here.
Unknown streams are streams we don't know the length of. They could be infinite or finite. Ordinary lists are like this.
RunTime streams have a defined finite length, which takes constant time to access.
CompileTime streams have their length as a compile time factor.
Empty streams are well, empty.
Having these different types can be useful. We might want a safe \"toVector\" function that takes only RunTime streams,
and immediately allocates the vector to that size before filling it.
But 'Stream' is indeed a GADT.
Currently there are 34 different types of streams. These range from simple streams just with a state
and a \"next_state\" function, to streams representing appended streams, concatenated streams, etc.
There's even streams that are a wrapper for 'Foldable' types, so instead of converting everything to a list,
you can just wrap your data in a stream and combine data of all different types seemlessly.
I believe there's lots of opportunity to optimise this library. Potentially
(if I got to understand the GHC API better) streams could carry around code blocks, which could compile
just in time (JIT) when required. This could allow for fast code to be generated in situations where there
are complex transformations, perhaps based on runtime branching, which the inliner can miss.
However, currently optimisation is limited. Indeed, the only optimisation I've to optimise the example
given in this documentation. But it does show the potential, and it is an extensible framework.
-}
module Data.Stream.Typed (
Stream, CompileTimeStream, RunTimeStream, UnknownStream, InfiniteStream,
CompileTime, RunTime, Length(Unknown, Infinite),
ToStream(toStream), Element,
-- $foldableToStreamDocs
runTimeFoldableToStream,
runTimeFoldableToStreamWithLength,
unknownFoldableToStream,
empty,
singleton,
AppendLength, append,
-- $zipDocs
zip, zipWith, ZipLength,
filter,
concat,
concatMap,
replicate,
iterate,
repeat,
cycle,
null,
unfoldr,
safeLength, SafeLength(KnownSafeLength, UnknownSafeLength, InfiniteSafeLength),
lengthRunTime,
safeHead, unsafeHead, maybeHead,
mixedConcat, ConcatLength,
memotise, strictMemotise,
wrapUnknown,
wrapRunTime
) where
import Data.Bits ((.&.))
import GHC.TypeLits (
Nat, natVal, KnownNat,
type (+), type (-), type (*), type (<=), type (<=?)
)
import Data.Proxy (Proxy)
import Prelude (
Functor, fmap, (<$),
Maybe(Just, Nothing),
Int, Integer, fromInteger, Integral,
Char,
Either(Left, Right),
(.),
const,
(+), (-), (*), (>=), (==), (>>), (/=),
($!), seq,
undefined,
error,
Bool (True, False),
(>>=),
return,
min,
snd,
id,
(&&),
Integral, fromIntegral,
flip
)
import Control.Applicative (
Applicative, pure, (<*>),
Alternative, (<|>)
)
import qualified Control.Applicative
import Control.Monad (
Monad, (>>=), return,
MonadPlus, mplus
)
import Data.Monoid (
Monoid, mempty, mappend, mconcat
)
import Control.Monad.Fix (fix)
import qualified Prelude
import Data.Foldable (
Foldable, foldr, length,
foldl', toList,
null, all
)
import Control.Arrow (first, second)
import Data.Proxy (Proxy(Proxy))
import Data.Maybe (catMaybes)
import GHC.Exts (
Constraint,
IsList, fromList, fromListN
)
import qualified GHC.Exts
import Data.Type.Bool (type If)
import Control.Monad (foldM_)
import Data.MonoTraversable.WrapMonoFoldable (WrappedMonoFoldable(WrappedMonoFoldable))
import Data.MonoTraversable (Element)
import Data.Semigroup (
Semigroup, (<>), stimes
)
import Data.Array (Array, )
import GHC.Exts (Item, lazy)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS
import Data.Word (Word8)
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as V hiding (length)
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VU hiding (length)
import Data.Vector.Unboxed (Unbox)
import Data.Generic.Enum (EnumFromTo(enumFromStepCount), EnumFrom(enumFromStep), Enum(type EnumNumT, type EnumIntegralT), fromEnum, toEnum)
import qualified Data.Generic.Enum as GE
data Length = Unknown | Infinite | Known KnownType
data KnownType = RunTimeLength | CompileTimeLength CompileTimeLengthType
data CompileTimeLengthType = NatLength Nat | Zero
type RunTime = Known RunTimeLength
type CompileTime n = Known (CompileTimeLength (NatLength n))
type Empty = Known (CompileTimeLength Zero)
type InfiniteStream a = Stream Infinite a
type UnknownStream a = Stream Unknown a
type RunTimeStream a = Stream RunTime a
type CompileTimeStream n a = Stream (CompileTime n) a
type EmptyStream a = Stream Empty a
data Stream (x :: Length) a where
EmptyStream :: EmptyStream a
SingletonStream :: a -> CompileTimeStream 1 a
CompileTimeSingleStream :: (KnownNat n) => (s -> (a,s)) -> s -> CompileTimeStream n a
RunTimeSingleStream :: Int -> (s -> (a,s)) -> s -> RunTimeStream a
UnknownSingleStream :: (s -> Maybe (a,s)) -> s -> UnknownStream a
InfiniteSingleStream :: (s -> (a,s)) -> s -> InfiniteStream a
CompileTimeConstantStream :: (KnownNat n) => a -> CompileTimeStream n a
RunTimeConstantStream :: Int -> a -> RunTimeStream a
UnknownConstantStream :: (s -> Maybe s) -> s -> a -> UnknownStream a
InfiniteConstantStream :: a -> InfiniteStream a
CompileTimeAppendStream :: (KnownNat n1, KnownNat n2) => CompileTimeStream n1 a -> CompileTimeStream n2 a -> CompileTimeStream (n1 + n2) a
RunTimeAppendStream :: Stream (Known l1) a -> Stream (Known l2) a -> RunTimeStream a
UnknownAppendStream :: Stream l1 a -> Stream l2 a -> UnknownStream a
InfiniteAppendStream :: Stream l1 a -> InfiniteStream a -> InfiniteStream a
UnknownUntypedStream :: Stream l a -> UnknownStream a
RunTimeUntypedStream :: Stream (Known l) a -> RunTimeStream a
CompileTimeFoldableStream :: (KnownNat n, Foldable t) => (b -> a) -> t b -> CompileTimeStream n a
RunTimeFoldableStream :: (Foldable t) => Int -> (b -> a) -> t b -> RunTimeStream a
UnknownFoldableStream :: (Foldable t) => (b -> Maybe a) -> t b -> UnknownStream a
InfiniteFoldableStream :: (Foldable t) => (b -> a) -> t b -> InfiniteStream a
CompileTimeZipStream :: (KnownNat n) => (a -> b -> c) -> Stream l1 a -> Stream l2 b -> CompileTimeStream n c
RunTimeZipStream :: (a -> b -> c) -> Stream l1 a -> Stream l2 b -> RunTimeStream c
UnknownZipStream :: (a -> b -> Maybe c) -> Stream l1 a -> Stream l2 b -> UnknownStream c
InfiniteZipStream :: (a -> b -> c) -> InfiniteStream a -> InfiniteStream b -> InfiniteStream c
CompileTimeConcatStream :: (KnownNat n1, KnownNat n2) => CompileTimeStream n1 (CompileTimeStream n2 a) -> CompileTimeStream (n1 * n2) a
RunTimeConcatStream :: Int -> Stream (Known l1) (Stream (Known l2) a) -> RunTimeStream a
UnknownConcatStream :: Stream l1 (Stream l2 a) -> UnknownStream a
InfiniteConcatStream :: InfiniteStream (Stream l2 a) -> InfiniteStream a
CompileTimeLazyMemotisedStream :: (KnownNat n) => V.Vector a -> CompileTimeStream n a
RunTimeLazyMemotisedStream :: V.Vector a -> RunTimeStream a
UnknownLazyMemotisedStream :: [a] -> UnknownStream a
InfiniteLazyMemotisedStream :: [a] -> InfiniteStream a
CompileTimeStrictMemotisedStream :: (KnownNat n, Unbox a) => VU.Vector a -> CompileTimeStream n a
RunTimeStrictMemotisedStream :: (Unbox a) => VU.Vector a -> RunTimeStream a
-- FiniteEnumStream :: (Enum a) => a -> EnumNumT a -> EnumIntegral a -> RunTimeStream a
-- InfiniteEnumStream :: (Enum a) => a -> EnumNumT a -> RunTimeStream a
pattern EmptyPattern :: () => (l ~ Empty) => Stream l a
pattern EmptyPattern = EmptyStream
pattern SingletonPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern SingletonPattern <- SingletonStream _
pattern CompileTimeSinglePattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeSinglePattern <- CompileTimeSingleStream _ _
pattern RunTimeSinglePattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeSinglePattern <- RunTimeSingleStream _ _ _
pattern UnknownSinglePattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownSinglePattern <- UnknownSingleStream _ _
pattern InfiniteSinglePattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteSinglePattern <- InfiniteSingleStream _ _
pattern CompileTimeConstantPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeConstantPattern <- CompileTimeConstantStream _
pattern RunTimeConstantPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeConstantPattern <- RunTimeConstantStream _ _
pattern UnknownConstantPattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownConstantPattern <- UnknownConstantStream _ _ _
pattern InfiniteConstantPattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteConstantPattern <- InfiniteConstantStream _
pattern CompileTimeAppendPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeAppendPattern <- CompileTimeAppendStream _ _
pattern RunTimeAppendPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeAppendPattern <- RunTimeAppendStream _ _
pattern UnknownAppendPattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownAppendPattern <- UnknownAppendStream _ _
pattern InfiniteAppendPattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteAppendPattern <- InfiniteAppendStream _ _
pattern CompileTimeZipPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeZipPattern <- CompileTimeZipStream _ _ _
pattern RunTimeZipPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeZipPattern <- RunTimeZipStream _ _ _
pattern UnknownZipPattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownZipPattern <- UnknownZipStream _ _ _
pattern InfiniteZipPattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteZipPattern <- InfiniteZipStream _ _ _
pattern UnknownUntypedPattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownUntypedPattern <- UnknownUntypedStream _
pattern RunTimeUntypedPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeUntypedPattern <- RunTimeUntypedStream _
pattern CompileTimeFoldablePattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeFoldablePattern <- CompileTimeFoldableStream _ _
pattern RunTimeFoldablePattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeFoldablePattern <- RunTimeFoldableStream _ _ _
pattern UnknownFoldablePattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownFoldablePattern <- UnknownFoldableStream _ _
pattern InfiniteFoldablePattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteFoldablePattern <- InfiniteFoldableStream _ _
pattern CompileTimeConcatPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeConcatPattern <- CompileTimeConcatStream _
pattern RunTimeConcatPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeConcatPattern <- RunTimeConcatStream _ _
pattern UnknownConcatPattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownConcatPattern <- UnknownConcatStream _
pattern InfiniteConcatPattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteConcatPattern <- InfiniteConcatStream _
pattern CompileTimeLazyMemotisedPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeLazyMemotisedPattern <- CompileTimeLazyMemotisedStream _
pattern RunTimeLazyMemotisedPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeLazyMemotisedPattern <- RunTimeLazyMemotisedStream _
pattern UnknownLazyMemotisedPattern :: () => (l ~ Unknown) => Stream l a
pattern UnknownLazyMemotisedPattern <- UnknownLazyMemotisedStream _
pattern InfiniteLazyMemotisedPattern :: () => (l ~ Infinite) => Stream l a
pattern InfiniteLazyMemotisedPattern <- InfiniteLazyMemotisedStream _
pattern CompileTimeStrictMemotisedPattern :: () => (l ~ CompileTime n, KnownNat n) => Stream l a
pattern CompileTimeStrictMemotisedPattern <- CompileTimeStrictMemotisedStream _
pattern RunTimeStrictMemotisedPattern :: () => (l ~ RunTime) => Stream l a
pattern RunTimeStrictMemotisedPattern <- RunTimeStrictMemotisedStream _
data StreamType (x :: Length) where
InfiniteStreamType :: StreamType Infinite
UnknownStreamType :: StreamType Unknown
RunTimeStreamType :: StreamType RunTime
CompileTimeStreamType :: (KnownNat n) => StreamType (CompileTime n)
EmptyStreamType :: StreamType Empty
empty :: EmptyStream a
empty = EmptyStream
singleton :: a -> CompileTimeStream 1 a
singleton = SingletonStream
replicate :: (Integral b) => b -> a -> RunTimeStream a
replicate n = RunTimeConstantStream (fromIntegral n)
unfoldr :: (b -> Maybe (a, b)) -> b -> UnknownStream a
unfoldr = UnknownSingleStream
{- $foldableToStreamDocs
Both 'runTimeFoldableToStream' and 'unknownFoldableToStream' wraps a Foldable data into a stream.
Which one you use is a matter of choice, but generally you should use 'runTimeFoldableToStream'
for structures like Vector which have a fixed and constant time list operation, and
'unknownFoldableToStream' for structures like list, particularly when you don't yet know their length.
By default 'runTimeFoldableToStream' just calls 'length' to work out it's length, but if say, you've got
a list but you already know it's length (and that it's finite), then 'runTimeFoldableToStreamWithLength'
might be the more appropriate choice.
-}
runTimeFoldableToStream :: (Foldable t) => t a -> RunTimeStream a
runTimeFoldableToStream x = RunTimeFoldableStream (length x) id x
runTimeFoldableToStreamWithLength :: (Foldable t) => Int -> t a -> RunTimeStream a
runTimeFoldableToStreamWithLength n x = RunTimeFoldableStream n id x
unknownFoldableToStream :: (Foldable t) => t a -> UnknownStream a
unknownFoldableToStream = UnknownFoldableStream pure
type family LengthT a = (r :: Length)
{-|
Add instances to the 'toStream' class to allow for easy conversion to streams.
Technically you could just use 'runTimeFoldableToStream' and ,'unknownFoldableToStream' to wrap data in
streams, but with this approach you can specialise for particular datatypes if appropriate.
-}
class ToStream a where
toStream :: a -> Stream (LengthT a) (Element a)
type instance LengthT [a] = Unknown
instance ToStream [a] where
toStream x = UnknownFoldableStream pure x
type instance Element (Array i e) = e
type instance LengthT (Array i e) = RunTime
instance ToStream (Array i e) where
toStream x = RunTimeFoldableStream (length x) id x
type instance LengthT BS.ByteString = RunTime
instance ToStream BS.ByteString where
toStream x = RunTimeFoldableStream (BS.length x) id (WrappedMonoFoldable x)
type instance LengthT BSL.ByteString = RunTime
instance ToStream BSL.ByteString where
toStream x = RunTimeFoldableStream ((fromIntegral . BSL.length) x) id (WrappedMonoFoldable x)
type instance LengthT (V.Vector a) = RunTime
instance ToStream (V.Vector a) where
toStream x = RunTimeLazyMemotisedStream x
type instance LengthT (VU.Vector a) = RunTime
instance (Unbox a) => ToStream (VU.Vector a) where
toStream x = RunTimeStrictMemotisedStream x
getStreamType :: forall l a. Stream l a -> StreamType l
getStreamType x = case x of
InfiniteSinglePattern -> InfiniteStreamType
InfiniteAppendPattern -> InfiniteStreamType
InfiniteFoldablePattern -> InfiniteStreamType
InfiniteConstantPattern -> InfiniteStreamType
InfiniteZipPattern -> InfiniteStreamType
InfiniteConcatPattern -> InfiniteStreamType
InfiniteLazyMemotisedPattern -> InfiniteStreamType
UnknownSinglePattern -> UnknownStreamType
UnknownAppendPattern -> UnknownStreamType
UnknownFoldablePattern -> UnknownStreamType
UnknownUntypedPattern -> UnknownStreamType
UnknownZipPattern -> UnknownStreamType
UnknownConstantPattern -> UnknownStreamType
UnknownConcatPattern -> UnknownStreamType
UnknownLazyMemotisedPattern -> UnknownStreamType
RunTimeSinglePattern -> RunTimeStreamType
RunTimeAppendPattern -> RunTimeStreamType
RunTimeFoldablePattern -> RunTimeStreamType
RunTimeConstantPattern -> RunTimeStreamType
RunTimeUntypedPattern -> RunTimeStreamType
RunTimeZipPattern -> RunTimeStreamType
RunTimeConcatPattern -> RunTimeStreamType
RunTimeLazyMemotisedPattern -> RunTimeStreamType
RunTimeStrictMemotisedPattern -> RunTimeStreamType
CompileTimeSinglePattern -> CompileTimeStreamType
CompileTimeAppendPattern -> CompileTimeStreamType
CompileTimeFoldablePattern -> CompileTimeStreamType
CompileTimeConstantPattern -> CompileTimeStreamType
CompileTimeZipPattern -> CompileTimeStreamType
CompileTimeConcatPattern -> CompileTimeStreamType
CompileTimeLazyMemotisedPattern -> CompileTimeStreamType
CompileTimeStrictMemotisedPattern -> CompileTimeStreamType
SingletonPattern -> CompileTimeStreamType
EmptyPattern -> EmptyStreamType
_ -> patternSynonymCatchAll
patternSynonymCatchAll :: a
patternSynonymCatchAll = error "Annoying catch all due to exhaustiveness checking not working for pattern synonyms. You should never reach here."
type family AppendLength (a :: Length) (b :: Length) where
AppendLength _ Infinite = Infinite
AppendLength Infinite _ = Infinite
AppendLength Empty y = y
AppendLength x Empty = x
AppendLength (CompileTime n1) (CompileTime n2) = CompileTime (n1 + n2)
AppendLength (Known l1) (Known l2) = RunTime
AppendLength _ _ = Unknown
data RunTimeWrapper a where
RunTimeWrapper :: Stream (Known l) a -> RunTimeWrapper a
data UnknownWrapper a where
UnknownWrapper :: Stream l a -> UnknownWrapper a
{-# INLINE [1] (<>-) #-}
(<>-) :: Stream l a -> Stream l a -> Stream l a
(<>-) x y = case (getStreamType x) of
InfiniteStreamType -> x
UnknownStreamType -> append x y
RunTimeStreamType -> append x y
CompileTimeStreamType -> error "This should never happen as this function should only be called by an appropriate rewrite rule."
EmptyStreamType -> EmptyStream
{-|
Whilst appending two streams of the same type always results in the same type,
appending two streams of different types can always be done, with the result type selected as appropriately as
possible.
-}
{-# INLINE append #-}
append :: forall l1 l2 a. Stream l1 a -> Stream l2 a -> Stream (AppendLength l1 l2) a
append x y = go (getStreamType x) (getStreamType y) where
go :: StreamType l1 -> StreamType l2 -> Stream (AppendLength l1 l2) a
go InfiniteStreamType _ = x
go EmptyStreamType _ = y
go _ EmptyStreamType = x
go _ InfiniteStreamType = mkInfiniteAppendStream x y
go CompileTimeStreamType CompileTimeStreamType = mkCompileTimeAppendStream x y
go CompileTimeStreamType RunTimeStreamType = mkRunTimeAppendStream x y
go RunTimeStreamType CompileTimeStreamType = mkRunTimeAppendStream x y
go RunTimeStreamType RunTimeStreamType = mkRunTimeAppendStream x y
go CompileTimeStreamType UnknownStreamType = mkUnknownAppendStream x y
go RunTimeStreamType UnknownStreamType = mkUnknownAppendStream x y
go UnknownStreamType CompileTimeStreamType = mkUnknownAppendStream x y
go UnknownStreamType RunTimeStreamType = mkUnknownAppendStream x y
go UnknownStreamType UnknownStreamType = mkUnknownAppendStream x y
mkCompileTimeAppendStream :: (KnownNat n1, KnownNat n2) => CompileTimeStream n1 a -> CompileTimeStream n2 a -> CompileTimeStream (n1 + n2) a
mkCompileTimeAppendStream = CompileTimeAppendStream
mkUnknownAppendStream :: Stream l1 a -> Stream l2 a -> UnknownStream a
mkUnknownAppendStream x y =
case (mkUnknownWrapper x, mkUnknownWrapper y) of
(UnknownWrapper x', UnknownWrapper y') -> UnknownAppendStream x' y'
mkInfiniteAppendStream :: Stream l1 a -> InfiniteStream a -> InfiniteStream a
mkInfiniteAppendStream = InfiniteAppendStream
mkRunTimeAppendStream :: Stream (Known l1') a -> Stream (Known l2') a -> RunTimeStream a
mkRunTimeAppendStream x y =
case (mkRunTimeWrapper x, mkRunTimeWrapper y) of
(RunTimeWrapper x', RunTimeWrapper y') -> RunTimeAppendStream x' y'
mkUnknownWrapper :: Stream l a -> UnknownWrapper a
mkUnknownWrapper (RunTimeUntypedStream x) = UnknownWrapper x
mkUnknownWrapper (UnknownUntypedStream x) = UnknownWrapper x
mkUnknownWrapper x = UnknownWrapper x
mkRunTimeWrapper :: Stream (Known l) a -> RunTimeWrapper a
mkRunTimeWrapper (RunTimeUntypedStream x) = RunTimeWrapper x
mkRunTimeWrapper x = RunTimeWrapper x
type Min (n1 :: Nat) (n2 :: Nat) = If (n1 <=? n2) n1 n2
type family ZipLength (a :: Length) (b :: Length) where
ZipLength Empty _ = Empty
ZipLength _ Empty = Empty
ZipLength x Infinite = x
ZipLength Infinite y = y
ZipLength (CompileTime n1) (CompileTime n2) = CompileTime (Min n1 n2)
ZipLength (Known l1) (Known l2) = RunTime
ZipLength _ _ = Unknown
data BooleanTest (a :: Bool) where
BooleanTestTrue :: BooleanTest True
BooleanTestFalse :: BooleanTest False
{- $zipDocs
Both 'zip' and 'zipWith' aren't optimised currently, they just convert both sides to lists and zip them sadly.
-}
zip :: Stream l1 a -> Stream l2 b -> Stream (ZipLength l1 l2) (a,b)
zip = zipWith (,)
zipWith :: forall l1 l2 a b c. (a -> b -> c) -> Stream l1 a -> Stream l2 b -> Stream (ZipLength l1 l2) c
zipWith f x y = go (getStreamType x) (getStreamType y) where
go :: StreamType l1 -> StreamType l2 -> Stream (ZipLength l1 l2) c
go EmptyStreamType _ = EmptyStream
go _ EmptyStreamType = EmptyStream
go CompileTimeStreamType InfiniteStreamType = mkCompileTime
go InfiniteStreamType CompileTimeStreamType = mkCompileTime
go RunTimeStreamType InfiniteStreamType = mkRunTime
go InfiniteStreamType RunTimeStreamType = mkRunTime
go UnknownStreamType InfiniteStreamType = mkUnknown
go InfiniteStreamType UnknownStreamType = mkUnknown
go InfiniteStreamType InfiniteStreamType = mkInfinite
go CompileTimeStreamType CompileTimeStreamType = go' where
go' :: forall n1 n2. (l1 ~ CompileTime n1, l2 ~ CompileTime n2) => Stream (CompileTime (Min n1 n2)) c
go' =
case (undefined :: (BooleanTest (n1 <=? n2))) of
BooleanTestTrue -> mkCompileTime
BooleanTestFalse -> mkCompileTime
go CompileTimeStreamType RunTimeStreamType = mkRunTime
go RunTimeStreamType CompileTimeStreamType = mkRunTime
go RunTimeStreamType RunTimeStreamType = mkRunTime
go CompileTimeStreamType UnknownStreamType = mkUnknown
go RunTimeStreamType UnknownStreamType = mkUnknown
go UnknownStreamType UnknownStreamType = mkUnknown
go UnknownStreamType CompileTimeStreamType = mkUnknown
go UnknownStreamType RunTimeStreamType = mkUnknown
mkCompileTime :: (ZipLength l1 l2 ~ CompileTime n, KnownNat n) => CompileTimeStream n c
mkCompileTime = CompileTimeZipStream f x y
mkRunTime :: (ZipLength l1 l2 ~ RunTime) => RunTimeStream c
mkRunTime =
case (mkUnknownWrapper x, mkUnknownWrapper y) of
(UnknownWrapper x', UnknownWrapper y') -> RunTimeZipStream f x' y'
mkUnknown :: (ZipLength l1 l2 ~ Unknown) => UnknownStream c
mkUnknown =
case (mkUnknownWrapper x, mkUnknownWrapper y) of
(UnknownWrapper x', UnknownWrapper y') -> UnknownZipStream (pure `compose2` f) x' y'
mkInfinite :: (l1 ~ Infinite, l2 ~ Infinite) => InfiniteStream c
mkInfinite = InfiniteZipStream f x y
foldableToVector :: Foldable t => t a -> V.Vector a
foldableToVector l =
V.create
(
do
v <- V.new (length l)
let f i x = V.write v i x >> return (i+1)
foldM_ f 0 l
return v
)
foldableToUnboxedVector :: (Unbox a) => Foldable t => t a -> VU.Vector a
foldableToUnboxedVector l =
VU.create
(
do
v <- VU.new (length l)
let f i x = VU.write v i x >> return (i+1)
foldM_ f 0 l
return v
)
{-|
As discussed in the intro to this module, by default streams when evaluated don't store their data.
'memotise' is effectively an \"id\" style function, but it takes the stream and stores it in either a
Vector or list. For @RunTimeStreams@, we use a Vector, as we know the length, but for @UnknownStreams@ and
@InfiniteStreams@ we use a list.
-}
memotise :: Stream l a -> Stream l a
memotise x = case getStreamType x of
InfiniteStreamType -> case x of
InfiniteLazyMemotisedStream _ -> x
_ -> InfiniteLazyMemotisedStream (toList x)
UnknownStreamType -> case x of
UnknownLazyMemotisedStream _ -> x
UnknownUntypedStream x -> wrapUnknown (memotise x)
_ -> UnknownLazyMemotisedStream (toList x)
RunTimeStreamType -> case x of
RunTimeLazyMemotisedStream _ -> x
RunTimeStrictMemotisedStream _ -> x
RunTimeUntypedStream x -> wrapRunTime (memotise x)
_ -> RunTimeLazyMemotisedStream (foldableToVector x)
CompileTimeStreamType -> CompileTimeLazyMemotisedStream (foldableToVector x)
EmptyStreamType -> EmptyStream
{-|
'strictMemotise' can be used for streams of Unboxed types. It then stores the data in
an unboxed vector. Note that this only works for streams of RunTime or CompileTime length,
obviously we can't put an infinite length vector in a vector, and we're not sure if unknown length
vectors are finite.
So in the case of infinite or unknown vectors, we just fall back to the normal 'memotise' behaviour.
-}
strictMemotise :: Unbox a => Stream l a -> Stream l a
strictMemotise x = case getStreamType x of
InfiniteStreamType -> memotise x
UnknownStreamType -> memotise x
RunTimeStreamType -> case x of
RunTimeStrictMemotisedStream _ -> x
RunTimeUntypedStream x -> wrapRunTime (strictMemotise x)
_ -> RunTimeStrictMemotisedStream (foldableToUnboxedVector x)
CompileTimeStreamType -> CompileTimeStrictMemotisedStream (foldableToUnboxedVector x)
EmptyStreamType -> EmptyStream
filter :: forall l a. (a -> Bool) -> Stream l a -> UnknownStream a
filter f x = go x where
go :: forall l. Stream l a -> UnknownStream a
go EmptyStream = emptyStream
go (SingletonStream e) = filterConstant e
go (CompileTimeConstantStream e) = filterConstant e
go (RunTimeConstantStream _ e) = filterConstant e
go (UnknownConstantStream _ _ e) = filterConstant e
go (InfiniteConstantStream e) = filterConstant e
go (CompileTimeSingleStream sf s) = mkUnknownSingleStreamFromLimited (length x) sf s
go (RunTimeSingleStream n sf s) = mkUnknownSingleStreamFromLimited n sf s
go (UnknownSingleStream sf s) = UnknownSingleStream h s where
h s = case (sf s) of
Nothing -> Nothing
result_plus_state@(Just (result, new_state)) ->
case (f result) of
True -> result_plus_state
False -> h new_state
go (InfiniteSingleStream sf s) = wrapUnknown (InfiniteSingleStream h s) where
h s =
let
result_plus_state@(result, new_state) = sf s
in
case (f result) of
True -> result_plus_state
False -> h new_state
go (CompileTimeAppendStream x y) = filterAppend x y
go (RunTimeAppendStream x y) = filterAppend x y
go (UnknownAppendStream x y) = filterAppend x y
go (InfiniteAppendStream x y) = filterAppend x y
go (UnknownUntypedStream x) = wrapUnknown (go x)
go (RunTimeUntypedStream x) = wrapUnknown (go x)
go (CompileTimeFoldableStream g x) = filterFoldable g x
go (RunTimeFoldableStream _ g x) = filterFoldable g x
go (UnknownFoldableStream g x) = filterFoldableMaybe g x
go (InfiniteFoldableStream g x) = filterFoldable g x
go (CompileTimeZipStream g x y) = filterZip g x y
go (RunTimeZipStream g x y) = filterZip g x y
go (UnknownZipStream g x y) = filterZipMaybe g x y
go (InfiniteZipStream g x y) = filterZip g x y
go (CompileTimeConcatStream x) = filterConcat x
go (RunTimeConcatStream _ x) = filterConcat x
go (UnknownConcatStream x) = filterConcat x
go (InfiniteConcatStream x) = filterConcat x
go (CompileTimeLazyMemotisedStream x) = filterFoldable id x
go (RunTimeLazyMemotisedStream x) = filterFoldable id x
go (UnknownLazyMemotisedStream x) = filterFoldable id x
go (InfiniteLazyMemotisedStream x) = filterFoldable id x
go (CompileTimeStrictMemotisedStream x) = filterFoldable id (WrappedMonoFoldable x)
go (RunTimeStrictMemotisedStream x) = filterFoldable id (WrappedMonoFoldable x)
filterConcat :: forall l1 l2. Stream l1 (Stream l2 a) -> UnknownStream a
filterConcat x = UnknownConcatStream (fmap go x)
filterZip :: forall b1 b2 l1 l2. (b1 -> b2 -> a) -> Stream l1 b1 -> Stream l2 b2 -> UnknownStream a
filterZip g = UnknownZipStream (filterMaybe `compose2` g)
filterZipMaybe :: forall b1 b2 l1 l2. (b1 -> b2 -> Maybe a) -> Stream l1 b1 -> Stream l2 b2 -> UnknownStream a
filterZipMaybe g = UnknownZipStream (\x y -> g x y >>= filterMaybe)
filterFoldableMaybe :: forall t b. Foldable t => (b -> Maybe a) -> t b -> Stream Unknown a
filterFoldableMaybe g = UnknownFoldableStream (\x -> g x >>= filterMaybe)
filterFoldable :: forall t b. Foldable t => (b -> a) -> t b -> Stream Unknown a
filterFoldable g = UnknownFoldableStream (filterMaybe . g)
filterMaybe :: a -> Maybe a
filterMaybe x = if (f x) then Just x else Nothing
filterAppend :: forall l1 l2. Stream l1 a -> Stream l2 a -> Stream Unknown a
filterAppend x y = UnknownAppendStream (go x) (go y)
mkUnknownSingleStreamFromLimited :: forall s. Int -> (s -> (a,s)) -> s -> UnknownStream a
mkUnknownSingleStreamFromLimited n sf s = UnknownSingleStream (h sf) (s, n) where
h :: (s -> (a,s)) -> (s, Int) -> Maybe (a, (s, Int))
h sf (s, n) = go s n where
go _ 0 = Nothing
go s n =
let
n_minus_1 = n - 1
(result, new_state) = sf s
in
case (f result) of
True -> Just (result, (new_state, n_minus_1))
False -> go new_state n_minus_1
filterConstant e = if f e then wrapUnknown x else emptyStream
emptyStream = UnknownUntypedStream EmptyStream
{-# INLINE [1] fmap' #-}
fmap' :: forall a b l. (a -> b) -> Stream l a -> Stream l b
fmap' f = go where
go :: forall l. Stream l a -> Stream l b
go (InfiniteSingleStream sf s) = InfiniteSingleStream ((first f) . sf) s
go (InfiniteAppendStream x y) = InfiniteAppendStream (go x) (go y)
go (UnknownSingleStream sf s) = UnknownSingleStream ((fmap (first f)) . sf) s
go (UnknownAppendStream x y) = UnknownAppendStream (go x) (go y)
go (UnknownUntypedStream x) = UnknownUntypedStream (go x)
go (RunTimeSingleStream n sf s) = RunTimeSingleStream n ((first f) . sf) s
go (RunTimeAppendStream x y) = RunTimeAppendStream (go x) (go y)
go (RunTimeUntypedStream x) = RunTimeUntypedStream (go x)
go (CompileTimeSingleStream sf s) = CompileTimeSingleStream ((first f) . sf) s
go (CompileTimeAppendStream x y) = CompileTimeAppendStream (go x) (go y)
go (SingletonStream x) = SingletonStream (f x)
go EmptyStream = EmptyStream
go (CompileTimeConstantStream x) = CompileTimeConstantStream (f x)
go (RunTimeConstantStream n x) = RunTimeConstantStream n (f x)
go (UnknownConstantStream sf s x) = UnknownConstantStream sf s (f x)
go (InfiniteConstantStream x) = InfiniteConstantStream (f x)
go (CompileTimeFoldableStream g x) = CompileTimeFoldableStream (f . g) x
go (RunTimeFoldableStream n g x) = RunTimeFoldableStream n (f . g) x
go (UnknownFoldableStream g x) = UnknownFoldableStream (fmap f . g) x
go (InfiniteFoldableStream g x) = InfiniteFoldableStream (f . g) x
go (CompileTimeZipStream g x y) = CompileTimeZipStream (f `compose2` g) x y
go (RunTimeZipStream g x y) = RunTimeZipStream (f `compose2` g) x y
go (UnknownZipStream g x y) = UnknownZipStream (fmap f `compose2` g) x y
go (InfiniteZipStream g x y) = InfiniteZipStream (f `compose2` g) x y
go (CompileTimeConcatStream l) = CompileTimeConcatStream (fmap' go l)
go (RunTimeConcatStream n l) = RunTimeConcatStream n (fmap' go l)
go (UnknownConcatStream l) = UnknownConcatStream (fmap' go l)
go (InfiniteConcatStream l) = InfiniteConcatStream (fmap' go l)
go (CompileTimeLazyMemotisedStream x) = CompileTimeFoldableStream f x
go (RunTimeLazyMemotisedStream x) = RunTimeFoldableStream (length x) f x
go (UnknownLazyMemotisedStream x) = UnknownFoldableStream (pure . f) x
go (InfiniteLazyMemotisedStream x) = InfiniteFoldableStream f x
go (CompileTimeStrictMemotisedStream x) = CompileTimeFoldableStream f (WrappedMonoFoldable x)
go (RunTimeStrictMemotisedStream x) = RunTimeFoldableStream (VU.length x) f (WrappedMonoFoldable x)
instance Functor (Stream l) where
fmap :: forall a b l. (a -> b) -> Stream l a -> Stream l b
fmap = fmap'
(<$) :: forall l a b. a -> Stream l b -> Stream l a
(<$) e = go where
go :: forall l. Stream l b -> Stream l a
go x = case (getStreamType x) of
InfiniteStreamType -> InfiniteConstantStream e
UnknownStreamType -> case x of
(UnknownAppendStream x y) -> UnknownAppendStream (go x) (go y)
(UnknownUntypedStream x) -> wrapUnknown (go x)
(UnknownConstantStream sf s _) -> UnknownConstantStream sf s e
(UnknownSingleStream sf s) -> UnknownConstantStream ((fmap snd) . sf) s e
(UnknownFoldableStream f l) -> UnknownFoldableStream (fmap (const e) . f) l
(UnknownZipStream f x y) -> UnknownZipStream (fmap (const e) `compose2` f) x y
(UnknownConcatStream x) -> UnknownConcatStream (fmap go x)
(UnknownLazyMemotisedStream l) -> UnknownFoldableStream (pure . (const e)) l
RunTimeStreamType -> case x of
(RunTimeConcatStream n x) -> RunTimeConcatStream n (fmap go x)
(RunTimeAppendStream x y) -> RunTimeAppendStream (go x) (go y)
_ -> RunTimeConstantStream (length x) e
CompileTimeStreamType -> CompileTimeConstantStream e
EmptyStreamType -> EmptyStream
type family SafeHead (l :: Length) = (f :: Bool) where
SafeHead Infinite = True
SafeHead (CompileTime _) = True
SafeHead _ = False
{-|
Just like Prelude's 'Prelude.head', errors out if there's a problem.
-}
unsafeHead :: Foldable t => t a -> a
unsafeHead = foldr const (error "Empty Foldable")
{-|
'safeHead' will only work on types which are guarenteed to have a head, like infinite streams
and compile time streams of length at least 1.
-}
safeHead :: (SafeHead l ~ True) => Stream l a -> a
safeHead = unsafeHead
{-|
Returns @Just a@ if list has a head, @Nothing@ otherwise.
-}
maybeHead :: Stream l a -> Maybe a
maybeHead x = case getStreamType x of
InfiniteStreamType -> Just (safeHead x)
UnknownStreamType -> foldr (\e _ -> Just e) Nothing x
RunTimeStreamType -> foldr (\e _ -> Just e) Nothing x
CompileTimeStreamType -> Just (safeHead x)
EmptyStreamType -> Nothing
class IsLengthType (l :: Length) where
getStreamTypeFromProxy :: Proxy l -> StreamType l
instance IsLengthType Empty where
getStreamTypeFromProxy _ = EmptyStreamType
instance (KnownNat n) => IsLengthType (CompileTime n) where
getStreamTypeFromProxy _ = CompileTimeStreamType
instance IsLengthType RunTime where
getStreamTypeFromProxy _ = RunTimeStreamType
instance IsLengthType Unknown where
getStreamTypeFromProxy _ = UnknownStreamType
instance IsLengthType Infinite where
getStreamTypeFromProxy _ = InfiniteStreamType
type family ConcatLength (l1 :: Length) (l2 :: Length) where
ConcatLength Empty _ = Empty
ConcatLength _ Empty = Empty
ConcatLength Infinite Infinite = Infinite
ConcatLength Infinite (CompileTime n1) = Infinite
ConcatLength (CompileTime n1) Infinite = Infinite
ConcatLength Infinite _ = Unknown
ConcatLength _ Infinite = Unknown
ConcatLength Unknown _ = Unknown
ConcatLength _ Unknown = Unknown
ConcatLength RunTime _ = RunTime
ConcatLength _ RunTime = RunTime
ConcatLength (CompileTime n1) (CompileTime n2) = CompileTime (n1 * n2)
{-|
'mixedConcat' is like the usual \"concat\", i.e. @[[a]] -> [a]@ except it works with nested
streams of different types, e.g. @RunTimeStream (UnknownStream a)@
-}
mixedConcat :: forall l1 l2 a. IsLengthType l2 => Stream l1 (Stream l2 a) -> Stream (ConcatLength l1 l2) a
mixedConcat x = case (getStreamType x, getStreamTypeFromProxy (undefined :: Proxy l2)) of
(EmptyStreamType, EmptyStreamType) -> EmptyStream
(EmptyStreamType, CompileTimeStreamType) -> EmptyStream
(EmptyStreamType, RunTimeStreamType) -> EmptyStream
(EmptyStreamType, UnknownStreamType) -> EmptyStream
(EmptyStreamType, InfiniteStreamType) -> EmptyStream
(CompileTimeStreamType, EmptyStreamType) -> EmptyStream
(RunTimeStreamType, EmptyStreamType) -> EmptyStream
(UnknownStreamType, EmptyStreamType) -> EmptyStream
(InfiniteStreamType, EmptyStreamType) -> EmptyStream
(InfiniteStreamType, InfiniteStreamType) -> safeHead x
(CompileTimeStreamType, InfiniteStreamType) -> safeHead x
(InfiniteStreamType, CompileTimeStreamType) -> InfiniteConcatStream x
(InfiniteStreamType, UnknownStreamType) -> UnknownConcatStream x
(InfiniteStreamType, RunTimeStreamType) -> UnknownConcatStream x
(UnknownStreamType, InfiniteStreamType) -> UnknownConcatStream x
(RunTimeStreamType, InfiniteStreamType) -> UnknownConcatStream x
(UnknownStreamType, UnknownStreamType) -> UnknownConcatStream x
(UnknownStreamType, CompileTimeStreamType) -> UnknownConcatStream x
(UnknownStreamType, RunTimeStreamType) -> UnknownConcatStream x
(CompileTimeStreamType, UnknownStreamType) -> UnknownConcatStream x
(RunTimeStreamType, UnknownStreamType) -> UnknownConcatStream x
(RunTimeStreamType, RunTimeStreamType) -> RunTimeConcatStream (foldLength x) x
(RunTimeStreamType, CompileTimeStreamType) -> let n1 = length x in if n1 /= 0 then RunTimeConcatStream (n1 * length (unsafeHead x)) x else RunTimeUntypedStream EmptyStream
(CompileTimeStreamType, RunTimeStreamType) -> RunTimeConcatStream (foldLength x) x
(CompileTimeStreamType, CompileTimeStreamType) -> CompileTimeConcatStream x
type family CanNormalConcat (l :: Length) = (b :: Bool) where
CanNormalConcat Infinite = True
CanNormalConcat Unknown = True
CanNormalConcat RunTime = True
CanNormalConcat (CompileTime _) = False
CanNormalConcat Empty = True
foldLength :: Stream (Known l1) (RunTimeStream a) -> Int
foldLength x = foldl' (+) 0 (fmap length x)
{-|
'concat' like a restricted version of 'mixedConcat' where the input and output types are the same.
Note 'concat' does not work on streams with compile time length, as with these streams the length is
included in the type so obviously concatenating them changes the type.
-}
concat :: (CanNormalConcat l ~ True) => Stream l (Stream l a) -> Stream l a
concat x = case (getStreamType x) of
InfiniteStreamType -> safeHead x
UnknownStreamType -> UnknownConcatStream x
RunTimeStreamType -> RunTimeConcatStream (foldLength x) x
EmptyStreamType -> EmptyStream
concatMap :: (CanNormalConcat l ~ True) => (a -> (Stream l b)) -> Stream l a -> Stream l b
concatMap f = concat . (fmap f)
monadAp :: (Monad m) => m (a -> b) -> m a -> m b
monadAp fs xs = fs >>= (\f -> fmap f xs)
instance Applicative (Stream Unknown) where
pure x = UnknownUntypedStream (SingletonStream x)
(<*>) = monadAp
instance Monad (Stream Unknown) where
(>>=) x f = concatMap f x
instance Applicative (Stream RunTime) where
pure x = RunTimeUntypedStream (SingletonStream x)
(<*>) = monadAp
instance Monad (Stream RunTime) where
(>>=) x f = concatMap f x
instance Monoid (Stream Unknown a) where
mempty = UnknownUntypedStream EmptyStream
mappend = append
mconcat x = concat (toStream x)
instance Monoid (Stream RunTime a) where
mempty = RunTimeUntypedStream EmptyStream
mappend = append
instance Monoid (Stream Empty a) where
mempty = EmptyStream
mappend _ _ = EmptyStream
mconcat _ = EmptyStream
instance Semigroup (Stream Unknown a) where
stimes n e = concat (UnknownUntypedStream (replicate n e))
instance Semigroup (Stream RunTime a) where
stimes n e = concat (replicate n e)
instance Semigroup (Stream Empty a) where
stimes _ _ = EmptyStream
instance Semigroup (Stream Infinite a) where
(<>) = const
stimes _ e = e
instance Alternative (Stream Unknown) where
empty = UnknownUntypedStream EmptyStream
(<|>) = append
instance Alternative (Stream RunTime) where
empty = RunTimeUntypedStream EmptyStream
(<|>) = append
instance (Alternative (Stream l), Monad (Stream l)) => MonadPlus (Stream l)
compose2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
compose2 f g x y = f (g x y)
{-|
Changes the type of any streams length to 'UnknownStream'.
Note that whilst now you can not distinguish this stream's length using the type system, it still
retains all it's previous behaviour. So if you 'wrapUnknown' a run time length stream, it's length function
will still work in constant time.
-}
wrapUnknown :: Stream l a -> UnknownStream a
wrapUnknown x = case (getStreamType x) of
InfiniteStreamType -> UnknownUntypedStream x
UnknownStreamType -> x
RunTimeStreamType -> case x of
RunTimeUntypedStream x -> UnknownUntypedStream x
_ -> UnknownUntypedStream x
CompileTimeStreamType -> UnknownUntypedStream x
EmptyStreamType -> UnknownUntypedStream EmptyStream
{-|
Like 'wrapUnknown' but instead to 'RunTimeStream'.
Of course, only runtime, compile time or empty streams can be converted to runtime streams, because runtime streams
must know their length.
-}
wrapRunTime :: Stream (Known l) a -> RunTimeStream a
wrapRunTime x = case (getStreamType x) of
RunTimeStreamType -> x
CompileTimeStreamType -> RunTimeUntypedStream x
EmptyStreamType -> RunTimeUntypedStream EmptyStream
compileTimeLength :: forall n t. (KnownNat n) => Stream (CompileTime (n :: Nat)) t -> Int
compileTimeLength _ = fromInteger (natVal (Proxy :: Proxy n))
data FoldInlineStage = FirstCall | ProxyCall | RecursiveCall
type family FoldInlineProxyNextStage x = (r :: FoldInlineStage) where
FoldInlineProxyNextStage FirstCall = ProxyCall
FoldInlineProxyNextStage _ = RecursiveCall
{-
There's some fancy optimisation going on here. What I noticed is that GHC can be amazingly fast if it can
inline. But it can't inline recursive functions. But unfortunately any branch of the function being
recursive makes it ineligable for inlining. I'd like to inline the simple cases.
So how this works is that all the functions get a dummy argument. What you'll notice is that
these functions are never called recursively with the dummy argument 'FirstCall'.
So if we specialise with this dummy argument, it will be non-recursive and inline.
Inlining is very important because it allows for all sorts of further optimisations.
Note I've only optimised foldl' like this. There is more work to be done!
-}
{-# INLINE [1] foldl''' #-}
foldl''' :: forall a b l. (b -> a -> b) -> b -> Stream l a -> b
foldl''' = goF' (Proxy :: Proxy FirstCall) where
{-# SPECIALISE INLINE goF' :: Proxy FirstCall -> (b -> a -> b) -> b -> Stream l a -> b #-}
goF' :: forall a l callStage. Proxy (callStage :: FoldInlineStage) -> (b -> a -> b) -> b -> Stream l a -> b
goF' cs f = goZ' cs where
{-# SPECIALISE INLINE goZ' :: Proxy FirstCall -> b -> Stream l a -> b #-}
goZ' :: forall l callStage. Proxy (callStage :: FoldInlineStage) -> b -> Stream l a -> b
goZ' cs z = go' cs where
{-# SPECIALISE INLINE go' :: Proxy FirstCall -> Stream l a -> b #-}
{-# SPECIALISE INLINE go' :: Proxy ProxyCall -> Stream l a -> b #-}
go' :: forall l callStage. Proxy (callStage :: FoldInlineStage) -> Stream l a -> b
go' _ x = case getStreamType x of
EmptyStreamType -> case x of
EmptyStream -> z
CompileTimeStreamType -> case x of
SingletonStream e -> z `f` e
CompileTimeConstantStream e -> applyNTimesL e (compileTimeLength x)
CompileTimeSingleStream sf s -> foldl'FixedLength sf (compileTimeLength x) s
CompileTimeAppendStream x y -> foldl'Two x y
CompileTimeFoldableStream g l -> doFoldableL g l
CompileTimeZipStream g x y -> foldl' f z (Prelude.zipWith g (toList x) (toList y))
CompileTimeConcatStream x -> concatFoldl' x
CompileTimeLazyMemotisedStream x -> foldl' f z x
CompileTimeStrictMemotisedStream x -> foldl' f z (WrappedMonoFoldable x)
RunTimeStreamType -> case x of
RunTimeSingleStream n sf s -> foldl'FixedLength sf n s
RunTimeAppendStream x y -> foldl'Two x y
RunTimeUntypedStream x -> goProxy x
RunTimeConstantStream n e -> applyNTimesL e n
RunTimeFoldableStream _ g l -> doFoldableL g l
RunTimeZipStream g x y -> foldl' f z (Prelude.zipWith g (toList x) (toList y))
RunTimeConcatStream _ x -> concatFoldl' x
RunTimeLazyMemotisedStream x -> foldl' f z x
RunTimeStrictMemotisedStream x -> foldl' f z (WrappedMonoFoldable x)
UnknownStreamType -> case x of
UnknownSingleStream sf s -> h z s where
h acc s = case sf s of
Just (r, next_s) ->
let next_acc = acc `f` r in next_acc `seq` h next_acc next_s
Nothing -> acc
UnknownAppendStream x y -> foldl'Two x y
UnknownConstantStream sf s e -> applyWhileJustState (`f` e) sf z s
UnknownFoldableStream g l -> foldl' h z l where
h x y = case g y of
Just y' -> f x y'
Nothing -> x
UnknownUntypedStream x -> goProxy x
UnknownZipStream g x y -> (foldl' f z . catMaybes) (Prelude.zipWith g (toList x) (toList y))
UnknownConcatStream x -> concatFoldl' x
UnknownLazyMemotisedStream x -> foldl' f z x
InfiniteStreamType -> error "Can't foldl' an infinite stream"
go :: forall l. Stream l a -> b
go = go' (Proxy :: Proxy RecursiveCall)
goProxy :: forall l. Stream l a -> b
goProxy = go' (Proxy :: Proxy (FoldInlineProxyNextStage callStage))
goZ :: forall l. b -> Stream l a -> b
goZ = goZ' (Proxy :: Proxy RecursiveCall)
goF :: forall a l. (b -> a -> b) -> b -> Stream l a -> b
goF = goF' (Proxy :: Proxy RecursiveCall)
foldl'Two :: forall l1 l2. Stream l1 a -> Stream l2 a -> b
foldl'Two x y = goZ (go x) y
foldl'FixedLength :: forall s. (s -> (a,s)) -> Int -> s -> b
foldl'FixedLength sf = go z where
go acc n s = case n of
0 -> acc
_ ->
let
(x, next_state) = sf s
next_acc = f acc x
in
next_acc `seq` go next_acc (n-1) next_state
applyNTimesL :: a -> Int -> b
applyNTimesL e n = applyNTimes (`f` e) z n
doFoldableL :: Foldable t => (s -> a) -> t s -> b
doFoldableL g l = foldl' (\x y -> f x (g y)) z l
concatFoldl' :: forall l1 l2. Stream l1 (Stream l2 a) -> b
concatFoldl' = goF goZ z
applyNTimes :: (a -> a) -> a -> Int -> a
applyNTimes f = go where
go acc i = case i of
0 -> acc
_ -> let next_acc = f acc in next_acc `seq` go next_acc (i - 1)
applyWhileJustState :: (a -> a) -> (s -> Maybe s) -> a -> s -> a
applyWhileJustState f sf = go where
go acc s = case sf s of
Nothing -> acc
Just next_state -> let next_acc = f acc in next_acc `seq` next_state `seq` go next_acc next_state
instance Foldable (Stream l) where
foldl' = foldl'''
foldr :: forall a b l. (a -> b -> b) -> b -> Stream l a -> b
foldr f z = go where
go :: forall l. Stream l a -> b
go str@(CompileTimeSingleStream sf s) = foldrFixedLength sf (compileTimeLength str) s
go (RunTimeSingleStream n sf s) = foldrFixedLength sf n s
go (UnknownSingleStream sf s) = h s where
h s' = case sf s' of
Just (r, next_s) -> r `f` (h next_s)
Nothing -> z
go (InfiniteSingleStream sf s) = h s where
h s' = let (r, next_s) = sf s' in r `f` (h next_s)
go (InfiniteAppendStream i1 i2) = foldrTwo i1 i2
go (UnknownAppendStream i1 i2) = foldrTwo i1 i2
go (RunTimeAppendStream i1 i2) = foldrTwo i1 i2
go (CompileTimeAppendStream i1 i2) = foldrTwo i1 i2
go EmptyStream = z
go (SingletonStream e) = e `f` z
go (UnknownUntypedStream x) = go x
go (RunTimeUntypedStream x) = go x
go (InfiniteConstantStream e) = e `f` (error "foldr of infinite constant stream using function strict in it's second argument, this can only diverge")
go (UnknownConstantStream sf s e) =
let
g = (e `f`)
in
case sf s of
Nothing -> z
Just next_s -> g (applyWhileJustState g sf z next_s)
go (RunTimeConstantStream n e) = applyNTimesR e n
go s@(CompileTimeConstantStream e) = applyNTimesR e (compileTimeLength s)
go (CompileTimeFoldableStream g l) = foldr (f . g) z l
go (RunTimeFoldableStream _ g l) = foldr (f . g) z l
go (UnknownFoldableStream g l) = foldr h z l where
h x y = case g x of
Just x' -> f x' y
Nothing -> y
go (InfiniteFoldableStream g l) = foldr (f . g) z l
go (CompileTimeZipStream g x y) = foldr f z (Prelude.zipWith g (toList x) (toList y))
go (RunTimeZipStream g x y) = foldr f z (Prelude.zipWith g (toList x) (toList y))
go (UnknownZipStream g x y) = (foldr f z . catMaybes) (Prelude.zipWith g (toList x) (toList y))
go (InfiniteZipStream g x y) = foldr f z (Prelude.zipWith g (toList x) (toList y))
go (CompileTimeConcatStream l) = concatFoldr l
go (RunTimeConcatStream _ l) = concatFoldr l
go (UnknownConcatStream l) = concatFoldr l
go (InfiniteConcatStream l) = concatFoldr l
go (CompileTimeLazyMemotisedStream x) = foldr f z x
go (RunTimeLazyMemotisedStream x) = foldr f z x
go (UnknownLazyMemotisedStream x) = foldr f z x
go (InfiniteLazyMemotisedStream x) = foldr f z x
go (CompileTimeStrictMemotisedStream x) = foldr f z (WrappedMonoFoldable x)
go (RunTimeStrictMemotisedStream x) = foldr f z (WrappedMonoFoldable x)
{-
This function does a foldR on a constant list.
Remember what foldr looks like on a constant list,
here's an example with length 4, and `f` is our function.
e `f` (e `f` (e `f` (e `f` z)))
foldr is not strict, and can short circuit by being lazy in its second argument.
However, `f` is pure, so it's laziness in the second argument depends entirely on the first.
So once we know `f` is not lazy with first argument `e`, it's never going to be lazy.
So we can strictly evaluate the rest at this point, without comprimising laziness.
Which is hopefully nice for performance, or at least space usage.
-}
applyNTimesR :: a -> Int -> b
applyNTimesR e n = case n of
0 -> z
_ ->
let
g = (e `f`)
in
g (applyNTimes g z (n-1))
foldrTwo :: forall l1 l2. Stream l1 a -> Stream l2 a -> b
foldrTwo i1 i2 = foldr f (foldr f z i2) i1
foldrFixedLength :: forall s. (s -> (a,s)) -> Int -> s -> b
foldrFixedLength sf = go where
go (0 :: Int) _ = z
go n s = let (r, next_s) = sf s in r `f` (go (n-1) next_s)
concatFoldr :: forall l1 l2. Stream l1 (Stream l2 a) -> b
concatFoldr l = foldr (.) id (fmap (\l' z' -> foldr f z' l') l) z
length x = case (safeLength x) of
KnownSafeLength n -> n
UnknownSafeLength n -> n
InfiniteSafeLength -> error "Length is infinite."
null x = case (getStreamType x) of
InfiniteStreamType -> False
UnknownStreamType -> foldr (\_ _ -> False) True x
RunTimeStreamType -> case x of
RunTimeAppendStream x y -> null x && null y
RunTimeUntypedStream x -> null x
RunTimeConcatStream _ x -> all null x
RunTimeSinglePattern -> isLength0
RunTimeFoldablePattern -> isLength0
RunTimeConstantPattern -> isLength0
RunTimeZipPattern -> isLength0
RunTimeLazyMemotisedPattern -> isLength0
RunTimeStrictMemotisedPattern -> isLength0
_ -> patternSynonymCatchAll
CompileTimeStreamType -> isLength0
EmptyStreamType -> True
where
isLength0 = (length x) == 0
data SafeLength = KnownSafeLength Int | UnknownSafeLength Int | InfiniteSafeLength
addSafeLength :: SafeLength -> SafeLength -> SafeLength
addSafeLength InfiniteSafeLength _ = InfiniteSafeLength
addSafeLength _ InfiniteSafeLength = InfiniteSafeLength
addSafeLength (KnownSafeLength x) (KnownSafeLength y) = KnownSafeLength (x+y)
addSafeLength (KnownSafeLength x) (UnknownSafeLength y) = UnknownSafeLength (x+y)
addSafeLength (UnknownSafeLength x) (KnownSafeLength y) = UnknownSafeLength (x+y)
addSafeLength (UnknownSafeLength x) (UnknownSafeLength y) = UnknownSafeLength (x+y)
minSafeLength :: SafeLength -> SafeLength -> SafeLength
minSafeLength InfiniteSafeLength y = y
minSafeLength x InfiniteSafeLength = x
minSafeLength (KnownSafeLength x) (KnownSafeLength y) = KnownSafeLength (min x y)
minSafeLength (KnownSafeLength x) (UnknownSafeLength y) = UnknownSafeLength (min x y)
minSafeLength (UnknownSafeLength x) (KnownSafeLength y) = UnknownSafeLength (min x y)
minSafeLength (UnknownSafeLength x) (UnknownSafeLength y) = UnknownSafeLength (min x y)
knownLength :: forall l a. Stream (Known l) a -> Int
knownLength x = case (safeLength x) of
KnownSafeLength n -> n
_ -> error "knownLength should always be a KnownSafeLength"
safeLength :: forall l a. Stream l a -> SafeLength
safeLength x = case (getStreamType x) of
InfiniteStreamType -> InfiniteSafeLength
UnknownStreamType -> case x of
UnknownAppendStream x y -> (safeLength x) `addSafeLength` (safeLength y)
UnknownUntypedStream x -> safeLength x
UnknownConcatStream x -> foldl' addSafeLength (KnownSafeLength 0) (fmap safeLength x)
UnknownLazyMemotisedStream x -> UnknownSafeLength (length x)
UnknownSinglePattern -> foldLength
UnknownFoldablePattern -> foldLength
UnknownZipPattern -> foldLength
UnknownConstantPattern -> foldLength
_ -> patternSynonymCatchAll
RunTimeStreamType -> KnownSafeLength (lengthRunTime x)
CompileTimeStreamType -> KnownSafeLength (compileTimeLength x)
EmptyStreamType -> KnownSafeLength 0
where
foldLength = UnknownSafeLength (foldl' (\c _ -> c+1) 0 x)
lengthRunTime :: forall l a. Stream (Known l) a -> Int
lengthRunTime x = case getStreamType x of
RunTimeStreamType -> case x of
(RunTimeSingleStream n _ _) -> n
(RunTimeAppendStream x y) -> lengthRunTime x + lengthRunTime y
(RunTimeFoldableStream n _ _) -> n
(RunTimeConstantStream n _) -> n
(RunTimeUntypedStream x) -> lengthRunTime x
(RunTimeZipStream _ x y) -> case (minSafeLength (safeLength x) (safeLength y)) of
KnownSafeLength n -> n
_ -> error "Length of a RunTimeZipStream should always be known"
(RunTimeConcatStream n _) -> n
(RunTimeLazyMemotisedStream x) -> V.length x
(RunTimeStrictMemotisedStream x) -> VU.length x
CompileTimeStreamType-> compileTimeLength x
EmptyStreamType -> 0
iterate :: (a -> a) -> a -> Stream Infinite a
iterate f x = InfiniteSingleStream g x where
g x = let y = f x in (y,y)
repeat :: a -> Stream Infinite a
repeat = InfiniteConstantStream
cycle :: Stream l a -> Stream Infinite a
cycle x = case getStreamType x of
InfiniteStreamType -> x
_ -> InfiniteConcatStream (InfiniteConstantStream x)
enumFromStepCount' :: (Enum a) => a -> EnumNumT a -> EnumIntegralT a -> RunTimeStream a
enumFromStepCount' start stepsize count = RunTimeSingleStream (fromIntegral count) (\x -> (x, toEnum (fromEnum x + stepsize))) start
enumFromStep' :: (Enum a) => a -> EnumNumT a -> InfiniteStream a
enumFromStep' start stepsize = InfiniteSingleStream (\x -> (x, toEnum (fromEnum x + stepsize))) start
type instance GE.Element (Stream l a) = a
instance (Enum a) => EnumFromTo (RunTimeStream a) where--
enumFromStepCount = enumFromStepCount'
instance (Enum a) => EnumFromTo (UnknownStream a) where
enumFromStepCount start stepsize count = wrapUnknown (enumFromStepCount' start stepsize count)
instance (Enum a) => EnumFrom (InfiniteStream a) where--
enumFromStep = enumFromStep'
instance (Enum a) => EnumFrom (UnknownStream a) where
enumFromStep start stepsize = wrapUnknown (enumFromStep' start stepsize)
{-# RULES
"protect fmap" fmap = fmap'
"protect foldl'" foldl' = foldl'''
"protect <>" (<>) = (<>-)
"fmap/semigroup" forall f xs ys. fmap' f (xs <>- ys) = (fmap' f xs) <>- (fmap' f ys)
"foldl'/semigroup" forall f z xs ys. foldl''' f z (xs <>- ys) = foldl''' f (foldl''' f z xs) ys
"foldl'/fmap" forall f z g x. foldl''' f z (fmap' g x) = let h x y = f x (g y) in foldl''' h z x
#-}