-- | -- Module: Data.Chimera -- Copyright: (c) 2018-2019 Andrew Lelechenko -- Licence: MIT -- Maintainer: Andrew Lelechenko -- -- Lazy infinite streams with O(1) indexing. {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} module Data.Chimera ( -- * Memoization memoize , memoizeFix -- * Chimera , Chimera , VChimera , UChimera -- * Construction , tabulate , tabulateFix , tabulateFix' , iterate , unfoldr , cycle , fromListWithDef , fromVectorWithDef -- * Manipulation , interleave -- * Elimination , index , toList -- * Monadic construction -- $monadic , tabulateM , tabulateFixM , tabulateFixM' , iterateM , unfoldrM -- * Subvectors -- $subvectors , mapSubvectors , traverseSubvectors , zipSubvectors , zipWithSubvectors , zipWithMSubvectors , sliceSubvectors ) where import Prelude hiding ((^), (*), div, fromIntegral, not, and, or, cycle, iterate, drop) import Control.Applicative import Control.Monad.Fix import Control.Monad.Trans.Class import qualified Control.Monad.Trans.State.Lazy as LazyState import Control.Monad.Zip import Data.Bits import qualified Data.Foldable as F import Data.Functor.Identity import qualified Data.Primitive.Array as A import qualified Data.Vector as V import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U #ifdef MIN_VERSION_mtl import Control.Monad.Reader (MonadReader, ask, local) #endif #ifdef MIN_VERSION_distributive import Data.Distributive #ifdef MIN_VERSION_adjunctions import qualified Data.Functor.Rep as Rep #endif #endif import Data.Chimera.FromIntegral -- $monadic -- Be careful: the stream is infinite, so -- monadic effects must be lazy -- in order to be executed in a finite time. -- -- For instance, lazy state monad works fine: -- -- >>> import Control.Monad.State.Lazy -- >>> ch = evalState (tabulateM (\i -> do modify (+ i); get)) 0 :: UChimera Word -- >>> take 10 (toList ch) -- [0,1,3,6,10,15,21,28,36,45] -- -- But the same computation in the strict state -- monad "Control.Monad.State.Strict" diverges. -- $subvectors -- Internally 'Chimera' consists of a number of subvectors. -- Following functions provide a low-level access to them. -- This ability is especially important for streams of booleans. -- -- Let us use 'Chimera' to memoize predicates @f1@, @f2@ @::@ 'Word' @->@ 'Bool'. -- Imagine them both already -- caught in amber as @ch1@, @ch2@ @::@ 'UChimera' 'Bool', -- and now we want to memoize @f3 x = f1 x && f2 x@ as @ch3@. -- One can do it in as follows: -- -- > ch3 = tabulate (\i -> index ch1 i && index ch2 i) -- -- There are two unsatisfactory things here. Firstly, -- even unboxed vectors store only one boolean per byte. -- We would rather reach out for 'Data.Bit.Bit' wrapper, -- which provides an instance of unboxed vector -- with one boolean per bit. Secondly, combining -- existing predicates by indexing them and tabulating again -- becomes relatively expensive, given how small and simple -- our data is. Fortunately, there is an ultra-fast 'Data.Bit.zipBits' -- to zip bit vectors. We can combine it altogether like this: -- -- > import Data.Bit -- > import Data.Bits -- > ch1 = tabulate (Bit . f1) -- > ch2 = tabulate (Bit . f2) -- > ch3 = zipWithSubvectors (zipBits (.&.)) ch1 ch2 -- | Lazy infinite streams with elements from @a@, -- backed by a 'G.Vector' @v@ (boxed, unboxed, storable, etc.). -- Use 'tabulate', 'tabulateFix', etc. to create a stream -- and 'index' to access its arbitrary elements -- in constant time. -- -- @since 0.2.0.0 newtype Chimera v a = Chimera { unChimera :: A.Array (v a) } deriving ( Functor -- ^ @since 0.2.0.0 , Foldable -- ^ @since 0.2.0.0 , Traversable -- ^ @since 0.2.0.0 ) -- | Streams backed by boxed vectors. -- -- @since 0.3.0.0 type VChimera = Chimera V.Vector -- | Streams backed by unboxed vectors. -- -- @since 0.3.0.0 type UChimera = Chimera U.Vector -- | 'pure' creates a constant stream. -- -- @since 0.2.0.0 instance Applicative (Chimera V.Vector) where pure a = Chimera $ A.arrayFromListN (bits + 1) $ G.singleton a : map (\k -> G.replicate (1 `shiftL` k) a) [0 .. bits - 1] (<*>) = zipWithSubvectors (<*>) #if __GLASGOW_HASKELL__ > 801 liftA2 f = zipWithSubvectors (liftA2 f) #endif -- | @since 0.3.1.0 instance Monad (Chimera V.Vector) where m >>= f = tabulate $ \w -> index (f (index m w)) w -- | @since 0.3.1.0 instance MonadFix (Chimera V.Vector) where mfix = tabulate . mfix . fmap index -- | @since 0.3.1.0 instance MonadZip (Chimera V.Vector) where mzip = zipWithSubvectors mzip mzipWith = zipWithSubvectors . mzipWith #ifdef MIN_VERSION_mtl -- | @since 0.3.1.0 instance MonadReader Word (Chimera V.Vector) where ask = tabulate id local = flip $ (tabulate .) . (.) . index #endif #ifdef MIN_VERSION_distributive -- | @since 0.3.1.0 instance Distributive (Chimera V.Vector) where distribute = tabulate . flip (fmap . flip index) collect f = tabulate . flip ((<$>) . (. f) . flip index) #ifdef MIN_VERSION_adjunctions -- | @since 0.3.1.0 instance Rep.Representable (Chimera V.Vector) where type Rep (Chimera V.Vector) = Word tabulate = tabulate index = index #endif #endif bits :: Int bits = finiteBitSize (0 :: Word) -- | Create a stream of values of a given function. -- Once created it can be accessed via 'index' or 'toList'. -- -- >>> ch = tabulate (^ 2) :: UChimera Word -- >>> index ch 9 -- 81 -- >>> take 10 (toList ch) -- [0,1,4,9,16,25,36,49,64,81] -- -- @since 0.2.0.0 tabulate :: G.Vector v a => (Word -> a) -> Chimera v a tabulate f = runIdentity $ tabulateM (pure . f) -- | Similar to 'V.generateM', but for raw arrays. generateArrayM :: Monad m => Int -> (Int -> m a) -> m (A.Array a) generateArrayM n f = A.arrayFromListN n <$> traverse f [0..n - 1] -- | Monadic version of 'tabulate'. -- -- @since 0.2.0.0 tabulateM :: (Monad m, G.Vector v a) => (Word -> m a) -> m (Chimera v a) tabulateM f = Chimera <$> generateArrayM (bits + 1) tabulateSubVector where tabulateSubVector 0 = G.singleton <$> f 0 tabulateSubVector i = G.generateM ii (\j -> f (int2word (ii + j))) where ii = 1 `unsafeShiftL` (i - 1) {-# SPECIALIZE tabulateM :: G.Vector v a => (Word -> Identity a) -> Identity (Chimera v a) #-} -- | For a given @f@ create a stream of values of a recursive function 'fix' @f@. -- Once created it can be accessed via 'index' or 'toList'. -- -- For example, imagine that we want to tabulate -- : -- -- >>> catalan n = if n == 0 then 1 else sum [ catalan i * catalan (n - 1 - i) | i <- [0 .. n - 1] ] -- -- Can we find @catalanF@ such that @catalan@ = 'fix' @catalanF@? -- Just replace all recursive calls to @catalan@ with @f@: -- -- >>> catalanF f n = if n == 0 then 1 else sum [ f i * f (n - 1 - i) | i <- [0 .. n - 1] ] -- -- Now we are ready to use 'tabulateFix': -- -- >>> ch = tabulateFix catalanF :: VChimera Integer -- >>> index ch 9 -- 4862 -- >>> take 10 (toList ch) -- [1,1,2,5,14,42,132,429,1430,4862] -- -- __Note__: Only recursive function calls with decreasing arguments are memoized. -- If full memoization is desired, use 'tabulateFix'' instead. -- -- @since 0.2.0.0 tabulateFix :: G.Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a tabulateFix uf = runIdentity $ tabulateFixM ((pure .) . uf . (runIdentity .)) -- | Fully memoizing version of 'tabulateFix'. -- This function will tabulate every recursive call, -- but might allocate a lot of memory in doing so. -- For example, the following piece of code calculates the -- highest number reached by the -- -- of a given number, but also allocates tens of gigabytes of memory, -- because the Collatz sequence will spike to very high numbers. -- -- >>> collatzF :: (Word -> Word) -> (Word -> Word) -- >>> collatzF _ 0 = 0 -- >>> collatzF f n = if n <= 2 then 4 else n `max` f (if even n then n `quot` 2 else 3 * n + 1) -- >>> -- >>> maximumBy (comparing $ index $ tabulateFix' collatzF) [0..1000000] -- ... -- -- Using 'memoizeFix' instead fixes the problem: -- -- >>> maximumBy (comparing $ memoizeFix collatzF) [0..1000000] -- 56991483520 -- -- @since 0.3.2.0 tabulateFix' :: G.Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a tabulateFix' uf = runIdentity $ tabulateFixM' ((pure .) . uf . (runIdentity .)) -- | Monadic version of 'tabulateFix'. -- There are no particular guarantees about the order of recursive calls: -- they may be executed more than once or executed in different order. -- That said, monadic effects must be idempotent and commutative. -- -- @since 0.2.0.0 tabulateFixM :: (Monad m, G.Vector v a) => ((Word -> m a) -> Word -> m a) -> m (Chimera v a) tabulateFixM = tabulateFixM_ Downwards {-# SPECIALIZE tabulateFixM :: G.Vector v a => ((Word -> Identity a) -> Word -> Identity a) -> Identity (Chimera v a) #-} -- | Monadic version of 'tabulateFix''. -- -- @since 0.3.3.0 tabulateFixM' :: forall m v a. (Monad m, G.Vector v a) => ((Word -> m a) -> Word -> m a) -> m (Chimera v a) tabulateFixM' = tabulateFixM_ Full {-# SPECIALIZE tabulateFixM' :: G.Vector v a => ((Word -> Identity a) -> Word -> Identity a) -> Identity (Chimera v a) #-} -- | Memoization strategy, only used by @tabulateFixM_@. data Strategy = Full | Downwards -- | Internal implementation for 'tabulateFixM' and 'tabulateFixM''. tabulateFixM_ :: forall m v a. (Monad m, G.Vector v a) => Strategy -> ((Word -> m a) -> Word -> m a) -> m (Chimera v a) tabulateFixM_ strat f = result where result :: m (Chimera v a) result = Chimera <$> generateArrayM (bits + 1) tabulateSubVector tabulateSubVector :: Int -> m (v a) tabulateSubVector 0 = G.singleton <$> case strat of Downwards -> fix f 0 Full -> f (\k -> flip index k <$> result) 0 tabulateSubVector i = subResult where subResult = G.generateM ii (\j -> f fixF (int2word (ii + j))) subResultBoxed = V.generateM ii (\j -> f fixF (int2word (ii + j))) ii = 1 `unsafeShiftL` (i - 1) fixF :: Word -> m a fixF k | k < int2word ii = flip index k <$> result | k <= int2word ii `shiftL` 1 - 1 = (`V.unsafeIndex` (word2int k - ii)) <$> subResultBoxed | otherwise = case strat of Downwards -> f fixF k Full -> flip index k <$> result -- | 'iterate' @f@ @x@ returns an infinite stream, generated by -- repeated applications of @f@ to @x@. -- -- >>> ch = iterate (+ 1) 0 :: UChimera Int -- >>> take 10 (toList ch) -- [0,1,2,3,4,5,6,7,8,9] -- -- @since 0.3.0.0 iterate :: G.Vector v a => (a -> a) -> a -> Chimera v a iterate f = runIdentity . iterateM (pure . f) -- | Similar to 'G.iterateNM'. iterateListNM :: forall a m. Monad m => Int -> (a -> m a) -> a -> m [a] iterateListNM n f = if n <= 0 then const (pure []) else go (n - 1) where go :: Int -> a -> m [a] go 0 s = pure [s] go k s = do fs <- f s (s :) <$> go (k - 1) fs -- | Monadic version of 'iterate'. -- -- @since 0.3.0.0 iterateM :: (Monad m, G.Vector v a) => (a -> m a) -> a -> m (Chimera v a) iterateM f seed = do nextSeed <- f seed let z = G.singleton seed zs <- iterateListNM bits go (G.singleton nextSeed) pure $ Chimera $ A.fromListN (bits + 1) (z : zs) where go vec = do nextSeed <- f (G.unsafeLast vec) G.iterateNM (G.length vec `shiftL` 1) f nextSeed {-# SPECIALIZE iterateM :: G.Vector v a => (a -> Identity a) -> a -> Identity (Chimera v a) #-} -- | 'unfoldr' @f@ @x@ returns an infinite stream, generated by -- repeated applications of @f@ to @x@, similar to `Data.List.unfoldr`. -- -- >>> ch = unfoldr (\acc -> (acc * acc, acc + 1)) 0 :: UChimera Int -- >>> take 10 (toList ch) -- [0,1,4,9,16,25,36,49,64,81] -- -- @since 0.3.3.0 unfoldr :: G.Vector v b => (a -> (b, a)) -> a -> Chimera v b unfoldr f = runIdentity . unfoldrM (pure . f) -- | This is not quite satisfactory, see https://github.com/haskell/vector/issues/447 unfoldrExactVecNM :: forall m a b v. (Monad m, G.Vector v b) => Int -> (a -> m (b, a)) -> a -> m (v b, a) unfoldrExactVecNM n f s = flip LazyState.evalStateT s $ do vec <- G.replicateM n f' seed <- LazyState.get pure (vec, seed) where f' :: LazyState.StateT a m b f' = do seed <- LazyState.get (value, newSeed) <- lift (f seed) LazyState.put newSeed pure value -- | Monadic version of 'unfoldr'. -- -- @since 0.3.3.0 unfoldrM :: (Monad m, G.Vector v b) => (a -> m (b, a)) -> a -> m (Chimera v b) unfoldrM f seed = do let go n s = if n >= bits then pure [] else do (vec, s') <- unfoldrExactVecNM (1 `shiftL` n) f s rest <- go (n + 1) s' pure $ vec : rest (z, seed') <- unfoldrExactVecNM 1 f seed zs <- go 0 seed' pure $ Chimera $ A.fromListN (bits + 1) (z : zs) interleaveVec :: G.Vector v a => v a -> v a -> v a interleaveVec as bs = G.generate (G.length as `shiftL` 1) (\n -> (if even n then as else bs) G.! (n `shiftR` 1)) -- | Intertleave two streams, sourcing even elements from the first one -- and odd elements from the second one. -- -- >>> ch = interleave (tabulate id) (tabulate (+ 100)) :: UChimera Word -- >>> take 10 (toList ch) -- [0,100,1,101,2,102,3,103,4,104] -- -- @since 0.3.3.0 interleave :: G.Vector v a => Chimera v a -> Chimera v a -> Chimera v a interleave (Chimera as) (Chimera bs) = Chimera $ A.arrayFromListN (bits + 1) vecs where vecs = A.indexArray as 0 : A.indexArray bs 0 : map (\i -> interleaveVec (A.indexArray as i) (A.indexArray bs i)) [1 .. bits - 1] -- | Index a stream in a constant time. -- -- >>> ch = tabulate (^ 2) :: UChimera Word -- >>> index ch 9 -- 81 -- -- @since 0.2.0.0 index :: G.Vector v a => Chimera v a -> Word -> a index (Chimera vs) i = (vs `A.indexArray` (bits - lz)) `G.unsafeIndex` word2int (i .&. complement ((1 `shiftL` (bits - 1)) `unsafeShiftR` lz)) where lz :: Int !lz = countLeadingZeros i {-# INLINE index #-} -- | Convert a stream to an infinite list. -- -- >>> ch = tabulate (^ 2) :: UChimera Word -- >>> take 10 (toList ch) -- [0,1,4,9,16,25,36,49,64,81] -- -- @since 0.3.0.0 toList :: G.Vector v a => Chimera v a -> [a] toList (Chimera vs) = foldMap G.toList vs measureOff :: Int -> [a] -> Either Int ([a], [a]) measureOff n | n <= 0 = Right . ([], ) | otherwise = go n where go m [] = Left m go 1 (x : xs) = Right ([x], xs) go m (x : xs) = case go (m - 1) xs of l@Left{} -> l Right (xs', xs'') -> Right (x : xs', xs'') measureOffVector :: G.Vector v a => Int -> v a -> Either Int (v a, v a) measureOffVector n xs | n <= l = Right (G.splitAt n xs) | otherwise = Left (n - l) where l = G.length xs -- | Create a stream of values from a given prefix, followed by default value -- afterwards. -- -- @since 0.3.3.0 fromListWithDef :: G.Vector v a => a -- ^ Default value -> [a] -- ^ Prefix -> Chimera v a fromListWithDef a = Chimera . A.fromListN (bits + 1) . go0 where go0 = \case [] -> G.singleton a : map (\k -> G.replicate (1 `shiftL` k) a) [0 .. bits - 1] x : xs -> G.singleton x : go 0 xs go k xs = case measureOff kk xs of Left l -> G.fromListN kk (xs ++ replicate l a) : map (\n -> G.replicate (1 `shiftL` n) a) [k + 1 .. bits - 1] Right (ys, zs) -> G.fromListN kk ys : go (k + 1) zs where kk = 1 `shiftL` k -- | Create a stream of values from a given prefix, followed by default value -- afterwards. -- -- @since 0.3.3.0 fromVectorWithDef :: G.Vector v a => a -- ^ Default value -> v a -- ^ Prefix -> Chimera v a fromVectorWithDef a = Chimera . A.fromListN (bits + 1) . go0 where go0 xs = case G.uncons xs of Nothing -> G.singleton a : map (\k -> G.replicate (1 `shiftL` k) a) [0 .. bits - 1] Just (y, ys) -> G.singleton y : go 0 ys go k xs = case measureOffVector kk xs of Left l -> (xs G.++ G.replicate l a) : map (\n -> G.replicate (1 `shiftL` n) a) [k + 1 .. bits - 1] Right (ys, zs) -> ys : go (k + 1) zs where kk = 1 `shiftL` k -- | Return an infinite repetition of a given vector. -- Throw an error on an empty vector. -- -- >>> ch = cycle (Data.Vector.fromList [4, 2]) :: VChimera Int -- >>> take 10 (toList ch) -- [4,2,4,2,4,2,4,2,4,2] -- -- @since 0.3.0.0 cycle :: G.Vector v a => v a -> Chimera v a cycle vec = case l of 0 -> error "Data.Chimera.cycle: empty list" _ -> tabulate (G.unsafeIndex vec . word2int . (`rem` l)) where l = int2word $ G.length vec -- | Memoize a function: -- repeating calls to 'memoize' @f@ @n@ -- would compute @f@ @n@ only once -- and cache the result in 'VChimera'. -- This is just a shortcut for 'index' '.' 'tabulate'. -- When @a@ is 'U.Unbox', it is faster to use -- 'index' ('tabulate' @f@ :: 'UChimera' @a@). -- -- prop> memoize f n = f n -- -- @since 0.3.0.0 memoize :: (Word -> a) -> (Word -> a) memoize = index @V.Vector . tabulate -- | For a given @f@ memoize a recursive function 'fix' @f@, -- caching results in 'VChimera'. -- This is just a shortcut for 'index' '.' 'tabulateFix'. -- When @a@ is 'U.Unbox', it is faster to use -- 'index' ('tabulateFix' @f@ :: 'UChimera' @a@). -- -- prop> memoizeFix f n = fix f n -- -- For example, imagine that we want to memoize -- : -- -- >>> fibo n = if n < 2 then toInteger n else fibo (n - 1) + fibo (n - 2) -- -- Can we find @fiboF@ such that @fibo@ = 'fix' @fiboF@? -- Just replace all recursive calls to @fibo@ with @f@: -- -- >>> fiboF f n = if n < 2 then toInteger n else f (n - 1) + f (n - 2) -- -- Now we are ready to use 'memoizeFix': -- -- >>> memoizeFix fiboF 10 -- 55 -- >>> memoizeFix fiboF 100 -- 354224848179261915075 -- -- This function can be used even when arguments -- of recursive calls are not strictly decreasing, -- but they might not get memoized. If this is not desired -- use 'tabulateFix'' instead. -- For example, here is a routine to measure the length of -- : -- -- >>> collatzF f n = if n <= 1 then 0 else 1 + f (if even n then n `quot` 2 else 3 * n + 1) -- >>> memoizeFix collatzF 27 -- 111 -- -- @since 0.3.0.0 memoizeFix :: ((Word -> a) -> Word -> a) -> (Word -> a) memoizeFix = index @V.Vector . tabulateFix -- | Map subvectors of a stream, using a given length-preserving function. -- -- @since 0.3.0.0 mapSubvectors :: (G.Vector u a, G.Vector v b) => (u a -> v b) -> Chimera u a -> Chimera v b mapSubvectors f = runIdentity . traverseSubvectors (pure . f) -- | Traverse subvectors of a stream, using a given length-preserving function. -- -- Be careful, because similar to 'tabulateM', only lazy monadic effects can -- be executed in a finite time: lazy state monad is fine, but strict one is -- not. -- -- @since 0.3.3.0 traverseSubvectors :: (G.Vector u a, G.Vector v b, Applicative m) => (u a -> m (v b)) -> Chimera u a -> m (Chimera v b) traverseSubvectors f (Chimera bs) = Chimera <$> traverse safeF bs where -- Computing vector length is cheap, so let's check that @f@ preserves length. safeF x = (\fx -> if G.length x == G.length fx then fx else error "traverseSubvectors: the function is not length-preserving") <$> f x {-# SPECIALIZE traverseSubvectors :: (G.Vector u a, G.Vector v b) => (u a -> Identity (v b)) -> Chimera u a -> Identity (Chimera v b) #-} -- | @since 0.3.0.0 zipSubvectors :: (G.Vector u a, G.Vector v b, G.Vector w c) => (u a -> v b -> w c) -> Chimera u a -> Chimera v b -> Chimera w c zipSubvectors = zipWithSubvectors {-# DEPRECATED zipSubvectors "Use zipWithSubvectors instead" #-} -- | Zip subvectors from two streams, using a given length-preserving function. -- -- @since 0.3.3.0 zipWithSubvectors :: (G.Vector u a, G.Vector v b, G.Vector w c) => (u a -> v b -> w c) -> Chimera u a -> Chimera v b -> Chimera w c zipWithSubvectors f = (runIdentity .) . zipWithMSubvectors ((pure .) . f) -- | Zip subvectors from two streams, using a given monadic length-preserving function. -- Caveats for 'tabulateM' and 'traverseSubvectors' apply. -- -- @since 0.3.3.0 zipWithMSubvectors :: (G.Vector u a, G.Vector v b, G.Vector w c, Applicative m) => (u a -> v b -> m (w c)) -> Chimera u a -> Chimera v b -> m (Chimera w c) zipWithMSubvectors f (Chimera bs1) (Chimera bs2) = Chimera <$> sequenceA (mzipWith safeF bs1 bs2) where -- Computing vector length is cheap, so let's check that @f@ preserves length. safeF x y = (\fx -> if G.length x == G.length fx then fx else error "traverseSubvectors: the function is not length-preserving") <$> f x y {-# SPECIALIZE zipWithMSubvectors :: (G.Vector u a, G.Vector v b, G.Vector w c) => (u a -> v b -> Identity (w c)) -> Chimera u a -> Chimera v b -> Identity (Chimera w c) #-} -- | Take a slice of 'Chimera', represented as a list on consecutive subvectors. -- -- @since 0.3.3.0 sliceSubvectors :: G.Vector v a => Int -- ^ How many initial elements to drop? -> Int -- ^ How many subsequent elements to take? -> Chimera v a -> [v a] sliceSubvectors off len = doTake len . doDrop off . F.toList . unChimera where doTake !_ [] = [] doTake n (x : xs) | n <= 0 = [] | n >= l = x : doTake (n - l) xs | otherwise = [G.take n x] where l = G.length x doDrop !_ [] = [] doDrop n (x : xs) | n <= 0 = x : xs | l <= n = doDrop (n - l) xs | otherwise = G.drop n x : xs where l = G.length x