Skip to content

Generalize scanned to non-empty folds #129

@amesgen

Description

@amesgen

scanned currently consumes folds in a way that does not let them make use of the fact that they are always going to be applied to at least one element.

For example, attaching the maximum of all elements so far via scanned is slightly annoying. Most naturally, for s :: Stream (Of a) m r, one would use scanned like this

L.purely S.scanned L.maximum s :: Stream (Of (a, Maybe a)) m r

The Maybe here is misleading; all values are actually Just. One can rectify this via e.g. S.mapMaybe sequence, but it would be better to avoid this.

Concretely, one can generalize scanned like this:

scanned' :: Monad m => (a -> x) -> (x -> a -> x) -> (x -> b) -> Stream (Of a) m r -> Stream (Of (a, b)) m r
scanned' begin step done = loop Nothing
  where
    loop m stream =
      case stream of
        Return r -> return r
        Effect mn -> Effect $ fmap (loop m) mn
        Step (a :> rest) ->
          case m of
            Nothing -> do
              let !x = begin a
                  !b = done x
              S.yield (a, b)
              loop (Just x) rest
            Just x -> do
              let !x' = step x a
                  !b = done x'
              S.yield (a, b)
              loop (Just x') rest

One can easily define scanned in terms of scanned', namely

scanned step begin done = scanned' (const begin) step done 

In the example above, one can now write

L1.purely scanned' L1.maximum :: Stream (Of (a, a)) m r

using import qualified Control.Foldl.NonEmpty as L1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions