HORIZON HASKELLDocslts/ghc-10.0.xfc4e5982026-10-11Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · fc4e598 · 2026-10-11

Modulestreamly-core-0.3.1Haskell2010

Streamly.Internal.Data.Scanr

Right scans.

Scanr vs Stream

A scan is a generalization of a stream. Like streams, a scan has an internal state. Unlike a stream, a scan produces an output only on an input, the output is a function of the scan state and the input. A scan produces at most one output on one input, in other words it is driven solely by the input, it cannot produce output on its own. A Scanr m () a can represent a Stream m a by supplying the scan with () inputs.

Scans vs pipes:

A scan is a simpler version of pipes. It can produce at most one output on one input. Whereas a pipe may produce output even without consuming anything or it can produce multiple outputs on a single input. Scans are simpler abstractions to think about compared to pipes and easier for the compiler to optimize and fuse.

Compositions

Append: this is the easiest. The behavior is simple even in presence of filtering (Skip) and termination (Stop). Skip translates to Skip, Stop translates to Stop.

demux: we select one of n scans to run. Behaviour with Skip is straight forward. Termination behavior has multiple options, stop when first one stops, stop when the last one stops, or stop when a selected one stops.

zip: run all and zip the outputs. If one of them Skips we Skip the output. If one of them stops we stop. It may be possible to collect the outputs as Just/Nothing values.

Another option could be if a Scan terminates do we want to start it again or not.

  • 1 type
  • 11 values

Type

1 declaration
datadata Scanr (m :: Type -> Type) a b
#

Represents a stateful transformation over an input stream of values of type a to outputs of type b in Monad m.

The constructor is Scan consume initial.

Constructors

Instances4Category, Arrow, Functor, Applicative
  • Monad m => Category (Scanr m)Defined in streamly-core-0.3.1 · Streamly.Internal.Data.Scanr
  • Monad m => Arrow (Scanr m)Defined in streamly-core-0.3.1 · Streamly.Internal.Data.Scanr
  • Functor m => Functor (Scanr m a)Defined in streamly-core-0.3.1 · Streamly.Internal.Data.Scanr

    fmap maps a pure function on a scan output.

    Example1 expression
    Stream.toList $ Stream.scanr (fmap (+1) Scanr.identity) $ Stream.fromList [1..5::Int][2,3,4,5,6]
  • Monad m => Applicative (Scanr m a)Defined in streamly-core-0.3.1 · Streamly.Internal.Data.Scanr

    Zips the outputs only when both scans produce outputs, discards otherwise.

Primitive Scans

5 declarations
valueidentity :: Monad m => Scanr m a a
#

An identity scan producing the same output as input.

Example1 expression
identity = Scanr.function Prelude.id
Example1 expression
Stream.toList $ Stream.scanr (Scanr.identity) $ Stream.fromList [1..5::Int][1,2,3,4,5]
valuefunction :: Monad m => (a -> b) -> Scanr m a b
#

A scan representing mapping of a pure function.

Example1 expression
Stream.toList $ Stream.scanr (Scanr.function (+1)) $ Stream.fromList [1..5::Int][2,3,4,5,6]
valuefunctionM :: Monad m => (a -> m b) -> Scanr m a b
#

A scan representing mapping of a monadic action.

Example1 expression
Stream.toList $ Stream.scanr (Scanr.functionM print) $ Stream.fromList [1..5::Int]12345[(),(),(),(),()]
valuefilter :: Monad m => (a -> Bool) -> Scanr m a a
#

A filtering scan using a pure predicate.

Example1 expression
Stream.toList $ Stream.scanr (Scanr.filter odd) $ Stream.fromList [1..5::Int][1,3,5]

Combinators

4 declarations
valuecompose :: Monad m => Scanr m b c -> Scanr m a b -> Scanr m a c
#

Connect two scans in series. Attach the first scan on the output of the second scan.

Example2 expressions
import Control.CategoryStream.toList $ Stream.scanr (Scanr.function (+1) >>> Scanr.function (+1)) $ Stream.fromList [1..5::Int][3,4,5,6,7]
valueteeWithMay
  1. :: Monad m
  2. => Maybe b -> Maybe c -> d
  3. -> Scanr m a b
  4. -> Scanr m a c
  5. -> Scanr m a d
#

Connect two scans in parallel. Distribute the input across two scans and zip their outputs. If the scan filters the output, Nothing is emitted otherwise Just is emitted. The scan stops if any of the scans stop.

Example1 expression
Stream.toList $ Stream.scanr (Scanr.teeWithMay (,) Scanr.identity (Scanr.function (\x -> x * x))) $ Stream.fromList [1..5::Int][(Just 1,Just 1),(Just 2,Just 4),(Just 3,Just 9),(Just 4,Just 16),(Just 5,Just 25)]
valueteeWith
  1. :: Monad m
  2. => b -> c -> d
  3. -> Scanr m a b
  4. -> Scanr m a c
  5. -> Scanr m a d
#

Produces an output only when both the scans produce an output. If any of the scans skips the output then the composed scan also skips. Stops when any of the scans stop.

Example1 expression
Stream.toList $ Stream.scanr (Scanr.teeWith (,) Scanr.identity (Scanr.function (\x -> x * x))) $ Stream.fromList [1..5::Int][(1,1),(2,4),(3,9),(4,16),(5,25)]

Scans

2 declarations