packages feed

streaming-nonempty-0.1.0.0: src/Streaming/NonEmpty.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Streaming.NonEmpty where

import Control.Category (id, (.))
import Control.Monad (Monad (return))
import Data.Bool (Bool)
import Data.Either
import Data.Eq ((==))
import Data.Function (($))
import Data.Functor (Functor, fmap, (<$>))
import Data.List.NonEmpty hiding (groupBy)
import Data.Semigroup (Semigroup, (<>))
import qualified Data.Semigroup as Semigroup
import Streaming (Of ((:>)), Stream, first, runIdentity, wrap)
import Streaming.Internal
import qualified Streaming.Prelude as S

-- | A stream with at least one functorial layer
newtype NEStream f m a = NEStream (f (Stream f m a)) deriving (Functor)

-- | Is a stream
toStream :: (Functor f, Monad m) => NEStream f m r -> Stream f m r
toStream (NEStream f) = wrap f

-- | fold1 is safe
fold1 :: Monad m => (a -> a -> a) -> NEStream (Of a) m r -> m (Of a r)
fold1 f (NEStream (x :> s)) = S.fold f x id s

-- | sconcat as in 'Data.Semigroup', with result
sconcat :: (Semigroup a, Monad m) => NEStream (Of a) m r -> m (Of a r)
sconcat = fold1 (<>)

-- | harvest the list with the result
toNonEmpty :: Monad m => NEStream (Of a) m r -> m (Of (NonEmpty a) r)
toNonEmpty (NEStream (x :> r)) = first (x :|) <$> S.toList r

-- | harvest the list only
toNonEmpty_ :: Monad m => NEStream (Of a) m r -> m (NonEmpty a)
toNonEmpty_ = fmap S.fst' . toNonEmpty

-- | sconcat as in 'Data.Semigroup'
sconcat_ :: (Monad m, Semigroup a) => NEStream (Of a) m r -> m a
sconcat_ (NEStream (x :> s)) = S.fold_ (<>) x id s

-- | group by some  equality  in non empty groups
groupBy
  :: (Monad m)
  => (a -> a -> Bool)
  -> Stream (Of a) m r
  -> Stream (NEStream (Of a) m) m r
groupBy equals = loop
  where
    loop stream = Effect $ do
      e <- S.next stream
      return $ case e of
        Left r -> Return r
        Right (a, p') ->
          Step $
            fmap loop (NEStream (a :> S.span (equals a) p'))

-- | what groupBy could be
groupByPure :: (a -> a -> Bool) -> [a] -> [NonEmpty a]
groupByPure equals = runIdentity . S.toList_ . S.mapped toNonEmpty . groupBy equals . S.each

-- | collapse semigroups by some equality
groupSemigroupBy :: (Semigroup a, Monad m) => (a -> a -> Bool) -> Stream (Of a) m r -> Stream (Of a) m r
groupSemigroupBy f = S.mapped sconcat . groupBy f

-- | what should be possible to do with 'groupBy'
-- fmap sconcat . groupBy equals
groupSemigroupByPure :: Semigroup b => (b -> b -> Bool) -> [b] -> [b]
groupSemigroupByPure equals = runIdentity . S.toList_ . groupSemigroupBy equals . S.each