Skip to content

Commit

Permalink
Merge pull request #2810 from composewell/scanl-module
Browse files Browse the repository at this point in the history
Add a Scanl module representing left scans
  • Loading branch information
harendra-kumar authored Aug 19, 2024
2 parents a10a505 + b8d33c5 commit c2e8f2d
Show file tree
Hide file tree
Showing 29 changed files with 6,821 additions and 155 deletions.
6 changes: 5 additions & 1 deletion benchmark/Streamly/Benchmark/Data/Fold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ toIntMapIO ::
toIntMapIO f = Stream.fold (FL.toContainerIO f FL.sum)

{-# INLINE toHashMapIO #-}
toHashMapIO :: (MonadIO m, Ord k, Num a, Hashable k) =>
toHashMapIO :: (MonadIO m, Num a, Hashable k
#if __GLASGOW_HASKELL__ == 810
, Eq k
#endif
) =>
(a -> k) -> Stream m a -> m (HashMap k a)
toHashMapIO f = Stream.fold (Fold.toHashMapIO f FL.sum)

Expand Down
3 changes: 2 additions & 1 deletion benchmark/Streamly/Benchmark/Data/Stream/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import System.Random (randomRIO)

import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.Pipe as Pipe
import qualified Streamly.Internal.Data.Scanl as Scanl
import qualified Streamly.Internal.Data.Scanr as Scanr

#ifdef USE_PRELUDE
Expand Down Expand Up @@ -405,7 +406,7 @@ foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
foldl' f z = Stream.fold (Fold.foldl' f z)

scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b
scanl' f z = Stream.scan (Fold.foldl' f z)
scanl' f z = Stream.scanl (Scanl.mkScanl f z)
#endif

{-# INLINE transformMapM #-}
Expand Down
8 changes: 5 additions & 3 deletions benchmark/Streamly/Benchmark/Data/Stream/Transform.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import Control.Monad.IO.Class (MonadIO(..))
import System.Random (randomRIO)

import qualified Streamly.Internal.Data.Fold as FL
import qualified Streamly.Internal.Data.Scanl as Scanl

import qualified Stream.Common as Common
import qualified Streamly.Internal.Data.Unfold as Unfold
Expand All @@ -40,6 +41,7 @@ import Control.DeepSeq (NFData(..))
import Data.Functor.Identity (Identity(..))
import qualified Prelude
import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.Scanl as Scanl
import qualified Streamly.Internal.Data.Stream.IsStream as Stream
import Streamly.Internal.Data.Time.Units
#else
Expand Down Expand Up @@ -91,7 +93,7 @@ scanl1M' n = composeN n $ Stream.scanl1M' (\b a -> return $ b + a)

{-# INLINE scan #-}
scan :: MonadIO m => Int -> Stream m Int -> m ()
scan n = composeN n $ Stream.scan FL.sum
scan n = composeN n $ Stream.scanl Scanl.sum

#ifdef USE_PRELUDE
{-# INLINE postscanl' #-}
Expand All @@ -105,7 +107,7 @@ postscanlM' n = composeN n $ Stream.postscanlM' (\b a -> return $ b + a) (return

{-# INLINE postscan #-}
postscan :: MonadIO m => Int -> Stream m Int -> m ()
postscan n = composeN n $ Stream.postscan FL.sum
postscan n = composeN n $ Stream.postscanl Scanl.sum

{-# INLINE sequence #-}
sequence :: MonadAsync m => Stream m (m Int) -> m ()
Expand Down Expand Up @@ -199,7 +201,7 @@ o_1_space_mappingX4 value =
sieveScan :: Monad m => Stream m Int -> Stream m Int
sieveScan =
Stream.mapMaybe snd
. Stream.scan (FL.foldlM' (\(primes, _) n -> do
. Stream.scanl (Scanl.mkScanlM (\(primes, _) n -> do
return $
let ps = takeWhile (\p -> p * p <= n) primes
in if all (\p -> n `mod` p /= 0) ps
Expand Down
6 changes: 6 additions & 0 deletions core/src/DocTestDataFold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
>>> :set -XFlexibleContexts
>>> import Control.Monad (void)
>>> import qualified Data.Foldable as Foldable
>>> import Data.Bifunctor(bimap)
>>> import Data.Function ((&))
>>> import Data.Functor.Identity (Identity, runIdentity)
>>> import Data.IORef (newIORef, readIORef, writeIORef)
Expand All @@ -13,6 +14,9 @@
>>> import Streamly.Data.Fold (Fold, Tee(..))
>>> import Streamly.Data.Stream (Stream)
>>> import qualified Data.Map as Map
>>> import qualified Data.Set as Set
>>> import qualified Data.IntSet as IntSet
>>> import qualified Streamly.Data.Array as Array
>>> import qualified Streamly.Data.Fold as Fold
>>> import qualified Streamly.Data.MutArray as MutArray
Expand All @@ -24,4 +28,6 @@
For APIs that have not been released yet.
>>> import qualified Streamly.Internal.Data.Fold as Fold
>>> import qualified Streamly.Internal.Data.Scanl as Scanl
>>> import qualified Streamly.Internal.Data.Stream as Stream
-}
36 changes: 36 additions & 0 deletions core/src/DocTestDataScanl.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{- $setup
>>> :m
>>> :set -XFlexibleContexts
>>> import Control.Monad (void)
>>> import qualified Data.Foldable as Foldable
>>> import Data.Bifunctor(bimap)
>>> import Data.Function ((&))
>>> import Data.Functor.Identity (Identity, runIdentity)
>>> import Data.IORef (newIORef, readIORef, writeIORef)
>>> import Data.Maybe (fromJust, isJust)
>>> import Data.Monoid (Endo(..), Last(..), Sum(..))
>>> import Prelude hiding (length, sum, minimum, maximum)
>>> import Streamly.Data.Array (Array)
>>> import Streamly.Data.Fold (Fold, Tee(..))
>>> import Streamly.Data.Stream (Stream)
>>> import qualified Data.Map as Map
>>> import qualified Data.Set as Set
>>> import qualified Data.IntSet as IntSet
>>> import qualified Streamly.Data.Array as Array
>>> import qualified Streamly.Data.Fold as Fold
>>> import qualified Streamly.Data.MutArray as MutArray
>>> import qualified Streamly.Data.Parser as Parser
>>> import qualified Streamly.Data.Scanl as Scanl
>>> import qualified Streamly.Data.Stream as Stream
>>> import qualified Streamly.Data.StreamK as StreamK
>>> import qualified Streamly.Data.Unfold as Unfold
For APIs that have not been released yet.
>>> import qualified Streamly.Internal.Data.Fold as Fold
>>> import qualified Streamly.Internal.Data.Ring as Ring
>>> import qualified Streamly.Internal.Data.Scanl as Scanl
>>> import qualified Streamly.Internal.Data.Stream as Stream
-}
1 change: 1 addition & 0 deletions core/src/DocTestDataStream.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
>>> import Streamly.Data.Stream (Stream)
>>> import qualified Streamly.Data.Array as Array
>>> import qualified Streamly.Data.Fold as Fold
>>> import qualified Streamly.Data.Scanl as Scanl
>>> import qualified Streamly.Data.Stream as Stream
>>> import qualified Streamly.Data.StreamK as StreamK
>>> import qualified Streamly.Data.Unfold as Unfold
Expand Down
31 changes: 22 additions & 9 deletions core/src/Streamly/Data/Fold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,11 @@ module Streamly.Data.Fold
, toMap
, toMapIO

{-
-- ** Key-value Scanners
, classify
, classifyIO
, classifyScan
, classifyScanIO
-}

-- ** Transforming the Monad
, morphInner
Expand All @@ -339,9 +341,9 @@ module Streamly.Data.Fold
-- | Transformations that combine two or more folds.

-- ** Scanning
, scan
, postscan
, scanMaybe
, scanl
, postscanl
-- , postscanlMaybe

-- ** Splitting
, splitWith
Expand Down Expand Up @@ -376,12 +378,14 @@ module Streamly.Data.Fold
-- based on the output of the previous fold.

-- ** Key-value Collectors
, demuxToMap
, demuxToMapIO
, demuxerToMap
, demuxerToMapIO

{-
-- ** Key-value Scanners
, demux
, demuxIO
, demuxScan
, demuxScanIO
-}

-- ** Nesting
, concatMap
Expand All @@ -398,6 +402,15 @@ module Streamly.Data.Fold
, variance
, stdDev
, serialWith
, classify
, classifyIO
, demux
, demuxIO
, demuxToMap
, demuxToMapIO
, scan
, postscan
, scanMaybe
)
where

Expand Down
189 changes: 189 additions & 0 deletions core/src/Streamly/Data/Scanl.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
{-# LANGUAGE CPP #-}
-- |
-- Module : Streamly.Data.Scanl
-- Copyright : (c) 2019 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : [email protected]
-- Stability : released
-- Portability : GHC
--

module Streamly.Data.Scanl
(
-- * Setup
-- | To execute the code examples provided in this module in ghci, please
-- run the following commands first.
--
-- $setup

-- * Scanl Type

Scanl -- (..)

-- * Constructors
, mkScanl
, mkScanlM
, mkScanl1
, mkScanl1M
, mkScanr

-- * Scans
-- ** Accumulators
-- | Scans that never terminate, these scans are much like strict left
-- folds. 'mconcat' is the fundamental accumulator. All other accumulators
-- can be expressed in terms of 'mconcat' using a suitable Monoid. Instead
-- of writing scans we could write Monoids and turn them into scans.

-- Monoids
, sconcat
, mconcat
, foldMap
, foldMapM

-- Reducers
, drain
-- , drainMapM
, length
, countDistinct
, countDistinctInt
-- , frequency
, sum
, product
, mean
, rollingHash
, rollingHashWithSalt

-- Collectors
, toList
, toListRev
, toSet
, toIntSet
, topBy

-- ** Non-Empty Accumulators
-- | Accumulators that do not have a default value, therefore, return
-- 'Nothing' on an empty stream.
, latest
, maximumBy
, maximum
, minimumBy
, minimum

-- ** Filtering Scanners
-- | Accumulators that are usually run as a scan using the 'potscanlMaybe'
-- combinator.
, findIndices
, elemIndices
, deleteBy
-- , uniq
, uniqBy
, nub
, nubInt

-- ** Terminating Folds
-- , satisfy
-- , maybe

, the

-- * Transformations
-- | Transformations are modifiers of scans. In the type @Scan m a b@, @a@
-- is the input type and @b@ is the output type. Transformations can be
-- applied either on the input side (contravariant) or on the output side
-- (covariant). Therefore, transformations have one of the following
-- general shapes:
--
-- * @... -> Scanl m a b -> Scanl m c b@ (input transformation)
-- * @... -> Scanl m a b -> Scanl m a c@ (output transformation)
--
-- The input side transformations are more interesting for scans. Most of
-- the following sections describe the input transformation operations on a
-- scan. When an operation makes sense on both input and output side we use
-- the prefix @l@ (for left) for input side operations and the prefix @r@
-- (for right) for output side operations.

-- ** Mapping on output
-- | The 'Functor' instance of a scan maps on the output of the scan:
--
-- >>> Stream.toList $ Stream.scanl (fmap show Scanl.sum) (Stream.enumerateFromTo 1 10)
-- ["0","1","3","6","10","15","21","28","36","45","55"]
--
, rmapM

-- ** Mapping on Input
, lmap
, lmapM

-- ** Filtering
, filter
, filterM

-- -- ** Mapping Filters
, mapMaybe
, catMaybes
, catLefts
, catRights
, catEithers

-- ** Trimming
, take
, takeEndBy
, takeEndBy_

-- ** Key-value Scanners
, classify
, classifyIO

-- ** Transforming the Monad
, morphInner

-- * Combinators
-- | Transformations that combine two or more scans.

-- ** Scanning
, scanl
, postscanl
, postscanlMaybe

-- ** Parallel Distribution
-- | The 'Applicative' instance distributes the input to both scans.

, teeWith
--, teeWithFst
--, teeWithMin
, tee
, distribute

-- ** Partitioning
-- | Direct items in the input stream to different scans using a binary
-- scan selector.

, partition
--, partitionByM
--, partitionByFstM
--, partitionByMinM
--, partitionBy

-- ** Unzipping
, unzip

-- * Dynamic Combinators
-- | The scan to be used is generated dynamically based on the input.

-- ** Key-value Scanners
, demux
, demuxIO
)
where

import Prelude
hiding (Foldable(..), filter, drop, dropWhile, take, takeWhile, zipWith,
map, mapM_, sequence, all, any,
notElem, head, last, tail,
reverse, iterate, init, and, or, lookup, (!!),
scanl, scanl1, replicate, concatMap, mconcat, unzip,
span, splitAt, break, mapM, maybe)

import Streamly.Internal.Data.Scanl

#include "DocTestDataScanl.hs"
Loading

0 comments on commit c2e8f2d

Please sign in to comment.