diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2017 Clinton Mead
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/src/Data/Stream.hs b/src/Data/Stream.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Stream.hs
@@ -0,0 +1,121 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+{-|
+The "Data.Stream.Typed" module contains more detailed documenation.
+
+This module simply imports functions from "Data.Stream.Typed" and modifies them so
+inputs and outputs are always of type 'Data.Stream.Typed.UnknownStream', which this
+module renames 'Stream' (yes, this clashes with 'Data.Stream.Typed.Stream' in
+"Data.Stream.Typed").
+
+Because of this, using this module more closely emulates how ordinary lists work,
+but you miss some of the compile time information you can get using the \"typed\" module.
+-}
+module Data.Stream (
+  Stream,
+  ToStream,
+  toStream,
+  runTimeFoldableToStream,
+  runTimeFoldableToStreamWithLength,
+  unknownFoldableToStream,
+  Element,
+  empty,
+  singleton,
+  append,
+  zip, zipWith,
+  filter,
+  concat,
+  concatMap,
+  replicate,
+  iterate,
+  repeat,
+  cycle,
+  null,
+  unfoldr,
+  safeLength, SafeLength(KnownSafeLength, UnknownSafeLength, InfiniteSafeLength),
+  maybeHead,
+  memotise
+  )
+where
+
+import Prelude (
+  (.),
+  Bool,
+  Int,
+  Integral,
+  Maybe,
+  Foldable
+  )
+
+import Data.Stream.Typed (
+  ToStream,
+  Element,
+  SafeLength(KnownSafeLength, UnknownSafeLength, InfiniteSafeLength),
+  wrapUnknown,
+  unfoldr,
+  unknownFoldableToStream
+  )
+
+import qualified Data.Stream.Typed as T
+
+type Stream a = T.UnknownStream a
+
+wrap :: T.Stream l a -> Stream a
+wrap = T.wrapUnknown
+
+toStream :: (ToStream a) => a -> Stream (Element a)
+toStream = wrap . T.toStream
+
+empty :: Stream a
+empty = wrap T.empty
+
+singleton :: a -> Stream a
+singleton = wrap . T.singleton
+
+append :: Stream a -> Stream a -> Stream a
+append = T.append
+
+zip :: Stream a -> Stream b -> Stream (a, b)
+zip = T.zip
+
+zipWith :: (a -> b -> c) -> Stream a -> Stream b -> Stream c
+zipWith = T.zipWith
+
+filter :: (a -> Bool) -> Stream a -> Stream a
+filter = T.filter
+
+concat :: Stream (Stream a) -> Stream a
+concat = T.concat
+
+concatMap :: (a -> Stream b) -> Stream a -> Stream b
+concatMap = T.concatMap
+
+replicate :: (Integral b) => b -> a -> Stream a
+replicate n x = wrap (T.replicate n x)
+
+iterate :: (a -> a) -> a -> Stream a
+iterate f x = wrap (T.iterate f x)
+
+repeat :: a -> Stream a
+repeat = wrap . repeat
+
+cycle :: Stream a -> Stream a
+cycle = wrap . cycle
+
+safeLength :: Stream a -> SafeLength
+safeLength = T.safeLength
+
+maybeHead :: Stream a -> Maybe a
+maybeHead = T.maybeHead
+
+memotise :: Stream a -> Stream a
+memotise = T.memotise
+
+runTimeFoldableToStream :: (Foldable t) => t a -> Stream a
+runTimeFoldableToStream = wrap . T.runTimeFoldableToStream
+
+runTimeFoldableToStreamWithLength :: (Foldable t) => Int -> t a -> Stream a
+runTimeFoldableToStreamWithLength n x = wrap (T.runTimeFoldableToStreamWithLength n x)
+
+null :: Stream a -> Bool
+null = T.null
diff --git a/src/Data/Stream/Typed.hs b/src/Data/Stream/Typed.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Stream/Typed.hs
@@ -0,0 +1,1487 @@
+{-# 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
+#-}
+
diff --git a/test/Tests.hs b/test/Tests.hs
new file mode 100644
--- /dev/null
+++ b/test/Tests.hs
@@ -0,0 +1,49 @@
+module Main (main) where
+
+import qualified Data.Generic.Enum as E
+import Data.Stream.Typed (RunTimeStream)
+import Data.Semigroup (Semigroup((<>)))
+
+import Data.Bits ((.&.))
+import Data.Foldable (foldl')
+
+import Criterion
+
+n :: Int
+n = 10000000
+
+f :: Int -> Int
+f x = x*(x .&. 3)
+
+g :: Int -> Int
+g x = x*(x .&. 7)
+
+xs :: Int -> RunTimeStream Int
+xs n = E.enumFromTo 1 n
+
+xl :: Int -> [Int]
+xl n = [1..n]
+
+sumG :: (Functor t, Foldable t, Semigroup (t Int)) => t Int -> Int
+sumG x = foldl' (+) 0 ((fmap f x) <> (fmap g x))
+
+sumS n = sumG (xs n)
+sumL n = sumG (xl n)
+
+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)
+
+main :: IO ()
+main = do
+  putStrLn ("n = " ++ show n)
+  putStrLn $ "Hand coded strict loop (Result = " ++ show (fast n) ++ " ):"
+  benchmark (nf fast n)
+  putStrLn $ "Using lists (Result = " ++ show (sumL n) ++ " ):"
+  benchmark (nf sumL n)
+  putStrLn $ "Using streams (Result = " ++ show (sumS n) ++ " ):"
+  benchmark (nf sumS n)
+
diff --git a/typed-streams.cabal b/typed-streams.cabal
new file mode 100644
--- /dev/null
+++ b/typed-streams.cabal
@@ -0,0 +1,51 @@
+name:                 typed-streams
+version:              0.1.0.0
+synopsis:             A stream based replacement for lists
+description:
+  This is an (incomplete) stream based replacement for lists, but already includes significant
+  functionality and can be faster than using lists in certain cases.
+  .
+  See "Data.Stream.Typed" for the most detailed documentation,
+  and "Data.Stream" for a simpler interface.
+license: MIT
+license-file: LICENSE
+copyright: Clinton Mead (2017)
+homepage:
+author:               Clinton Mead
+maintainer:           clintonmead@gmail.com
+category:             Data
+build-type:           Simple
+cabal-version:        >=1.10
+tested-with: GHC == 8.0.2
+bug-reports: https://github.com/clintonmead/generic-enum/issues
+
+library
+  exposed-modules: Data.Stream.Typed, Data.Stream
+  build-depends:
+    base == 4.9.*,
+    array == 0.5.*,
+    bytestring == 0.10.*,
+    make-monofoldable-foldable == 0.1.*,
+    mono-traversable == 1.0.*,
+    generic-enum == 0.1.*,
+    ghc-typelits-knownnat == 0.2.*,
+    vector == 0.12.*
+  hs-source-dirs:       src
+  default-language:     Haskell2010
+
+Test-Suite tests
+  type: exitcode-stdio-1.0
+  other-modules: Data.Stream.Typed, Data.Stream
+  main-is: Tests.hs
+  build-depends:
+    base == 4.9.*,
+    array == 0.5.*,
+    bytestring == 0.10.*,
+    make-monofoldable-foldable == 0.1.*,
+    mono-traversable == 1.0.*,
+    generic-enum == 0.1.*,
+    ghc-typelits-knownnat == 0.2.*,
+    vector == 0.12.*,
+    criterion == 1.1.*
+  hs-source-dirs:       src, test
+  default-language:     Haskell2010
