packages feed

fresnel-0.1.0.0: src/Fresnel/Fold.hs

{-# LANGUAGE RankNTypes #-}
module Fresnel.Fold
( -- * Folds
  Fold
, IsFold
  -- * Construction
, folded
, unfolded
, folding
, foldring
, foldMapping
, foldMap1ing
, ignored
, backwards
, iterated
, filtered
, repeated
, replicated
, cycled
, takingWhile
  -- * Elimination
, has
, hasn't
, foldMapOf
, foldMapByOf
, foldrOf
, foldlOf'
, foldOf
, foldByOf
, sequenceOf_
, traverseOf_
, forOf_
, toListOf
, anyOf
, allOf
, noneOf
, andOf
, orOf
, productOf
, sumOf
, altOf
, asumOf
, concatOf
, concatMapOf
, elemOf
, notElemOf
, lengthOf
, nullOf
, notNullOf
, firstOf
, lastOf
, minimumOf
, minimumByOf
, maximumOf
, maximumByOf
, findOf
, findMOf
, lookupOf
, previews
, preview
, (^?)
, Failover(..)
, Union(..)
) where

import Control.Applicative (Alternative(..))
import Control.Applicative.Backwards
import Data.Foldable (traverse_)
import Data.Functor (void)
import Data.Functor.Contravariant
import Data.Monoid
import Data.Profunctor
import Data.Profunctor.Unsafe ((#.), (.#))
import Fresnel.Bifunctor.Contravariant
import Fresnel.Fold1.Internal (IsFold1)
import Fresnel.Functor.Traversed
import Fresnel.Monoid.Cons as Cons
import Fresnel.Monoid.Fork as Fork
import Fresnel.Monoid.Snoc as Snoc
import Fresnel.Optic
import Fresnel.OptionalFold.Internal (IsOptionalFold)
import Fresnel.Traversal (IsTraversal, ignored, traversal)

-- Folds

type Fold s a = forall p . IsFold p => Optic' p s a

class (IsOptionalFold p, IsFold1 p, IsTraversal p) => IsFold p

instance Monoid r => IsFold (Forget r)
instance (Applicative f, Traversable f, Contravariant f) => IsFold (Star f)


-- Construction

folded :: Foldable f => Fold (f a) a
folded = rphantom . traversal traverse_

unfolded :: (s -> Maybe (a, s)) -> Fold s a
unfolded coalg = rphantom . traversal (\ f -> let loop = maybe (pure ()) (\ (a, s) -> f a *> loop s) . coalg in loop)

folding :: Foldable f => (s -> f a) -> Fold s a
folding f = contrabimap f (const ()) . traversal traverse_

foldring :: (forall f . Applicative f => (a -> f u -> f u) -> f v -> s -> f w) -> Fold s a
foldring fr = rphantom . traversal (\ f -> fr (\ a -> (f a *>)) (pure v)) where
  v = error "foldring: value used"

-- | Make a 'Fold' by lifting a 'foldMap'-like function.
--
-- @
-- 'foldMapping' 'foldMap' = 'folded'
-- @
foldMapping :: (forall m . Monoid m => (a -> m) -> (s -> m)) -> Fold s a
foldMapping fm = rphantom . traversal (\ f -> getAp . fm (Ap . void . f))

-- | Make a 'Fold' by lifting a 'foldMap1'-like function.
foldMap1ing :: (forall m . Semigroup m => (a -> m) -> (s -> m)) -> Fold s a
foldMap1ing fm = rphantom . traversal (\ f -> getAp . fm (Ap . void . f))

backwards :: Fold s a -> Fold s a
backwards o = rphantom . traversal (\ f -> forwards . traverseOf_ o (Backwards #. f))

iterated :: (a -> a) -> Fold a a
iterated f = rphantom . traversal (\ g -> let loop a = g a *> loop (f a) in loop)

filtered :: (a -> Bool) -> Fold a a
filtered p = folding (\ a -> if p a then Just a else Nothing)

-- | An infinite fold repeatedly producing its input.
--
-- @
-- 'toListOf' 'repeated' a = 'repeat' a
-- @
repeated :: Fold a a
repeated = rphantom . traversal (\ f a -> let loop = f a *> loop in loop)

-- | A fold producing its input a finite number of times.
--
-- @
-- 'toListOf' ('replicated' n) a = 'replicate' n a
-- @
replicated :: Int -> Fold a a
replicated n0 = rphantom . traversal (\ f -> let loop n a = if n <= 0 then pure () else f a *> loop (n - 1) a in loop n0)

cycled :: Fold s a -> Fold s a
cycled f = foldring (\ cons _ s -> let loop = foldrOf f cons loop s in loop)

takingWhile :: (a -> Bool) -> Fold s a -> Fold s a
takingWhile p f = foldring (\ cons nil -> foldrOf f (\ a as -> if p a then cons a as else nil) nil)


-- Elimination

has :: Fold s a -> (s -> Bool)
has = notNullOf

hasn't :: Fold s a -> (s -> Bool)
hasn't = nullOf


foldMapOf :: Monoid m => Fold s a -> ((a -> m) -> (s -> m))
foldMapOf o = runForget #. o .# Forget

foldMapByOf :: Fold s a -> ((r -> r -> r) -> r -> (a -> r) -> (s -> r))
foldMapByOf o fork nil leaf s = runFork (runForget (o (Forget Fork.singleton)) s) fork leaf nil

foldrOf :: Fold s a -> ((a -> r -> r) -> r -> s -> r)
foldrOf o cons nil s = runCons (runForget (o (Forget Cons.singleton)) s) cons nil

foldlOf' :: Fold s a -> ((r -> a -> r) -> r -> s -> r)
foldlOf' o snoc nil s = runSnoc (runForget (o (Forget Snoc.singleton)) s) snoc nil

foldOf :: Monoid a => Fold s a -> (s -> a)
foldOf o = foldMapOf o id

foldByOf :: Fold s a -> ((a -> a -> a) -> a -> (s -> a))
foldByOf o fork nil s = runFork (runForget (o (Forget Fork.singleton)) s) fork id nil

sequenceOf_ :: Applicative f => Fold s (f a) -> (s -> f ())
sequenceOf_ o = runTraversed . foldMapOf o Traversed

traverseOf_ :: Applicative f => Fold s a -> ((a -> f r) -> (s -> f ()))
traverseOf_ o f = runTraversed . foldMapOf o (Traversed #. f)

forOf_ :: Applicative f => Fold s a -> (s -> (a -> f r) -> f ())
forOf_ o = flip (traverseOf_ o)

toListOf :: Fold s a -> s -> [a]
toListOf o = foldrOf o (:) []

anyOf :: Fold s a -> (a -> Bool) -> (s -> Bool)
anyOf o = foldMapByOf o (||) False

allOf :: Fold s a -> (a -> Bool) -> (s -> Bool)
allOf o = foldMapByOf o (&&) True

noneOf :: Fold s a -> (a -> Bool) -> (s -> Bool)
noneOf o p = anyOf o (not . p)

andOf :: Fold s Bool -> (s -> Bool)
andOf o = getAll #. foldMapOf o All

orOf :: Fold s Bool -> (s -> Bool)
orOf o = getAny #. foldMapOf o Any

productOf :: Num a => Fold s a -> (s -> a)
productOf o = getProduct #. foldMapOf o Product

sumOf :: Num a => Fold s a -> (s -> a)
sumOf o = getSum #. foldMapOf o Sum

altOf :: Alternative f => Fold s a -> (s -> f a)
altOf o = getAlt #. foldMapOf o (Alt #. pure)

asumOf :: Alternative f => Fold s (f a) -> (s -> f a)
asumOf o = getAlt #. foldMapOf o Alt

concatOf :: Fold s [a] -> (s -> [a])
concatOf = foldOf

concatMapOf :: Fold s a -> ((a -> [r]) -> (s -> [r]))
concatMapOf = foldMapOf

elemOf :: Eq a => Fold s a -> a -> s -> Bool
elemOf o = anyOf o . (==)

notElemOf :: Eq a => Fold s a -> a -> s -> Bool
notElemOf o = noneOf o . (==)

lengthOf :: Fold s a -> (s -> Int)
lengthOf o = foldrOf o (const (+ 1)) 0

nullOf :: Fold s a -> (s -> Bool)
nullOf o = foldrOf o (\ _ _ -> False) True

notNullOf :: Fold s a -> (s -> Bool)
notNullOf o = foldrOf o (\ _ _ -> True) False

firstOf :: Fold s a -> (s -> Maybe a)
firstOf o = foldrOf o (\ a _ -> Just a) Nothing

lastOf :: Fold s a -> (s -> Maybe a)
lastOf o = getLast #. foldMapOf o (Last #. Just)

minimumOf :: Ord a => Fold s a -> (s -> Maybe a)
minimumOf o = minimumByOf o compare

minimumByOf :: Fold s a -> (a -> a -> Ordering) -> (s -> Maybe a)
minimumByOf o cmp = foldlOf' o (\ a b -> Just (case a of
  Nothing -> b
  Just a
    | GT <- cmp a b -> b
    | otherwise     -> a)) Nothing

maximumOf :: Ord a => Fold s a -> (s -> Maybe a)
maximumOf o = maximumByOf o compare

maximumByOf :: Fold s a -> (a -> a -> Ordering) -> (s -> Maybe a)
maximumByOf o cmp = foldlOf' o (\ a b -> Just (case a of
  Nothing -> b
  Just a
    | LT <- cmp a b -> b
    | otherwise     -> a)) Nothing

findOf :: Fold s a -> (a -> Bool) -> (s -> Maybe a)
findOf o p = foldrOf o (\ a as -> if p a then Just a else as) Nothing

findMOf :: Monad m => Fold s a -> (a -> m Bool) -> (s -> m (Maybe a))
findMOf o p = foldrOf o (\ a as -> do
  c <- p a
  if c then
    return (Just a)
  else
    as) (return Nothing)

lookupOf :: Eq k => Fold s (k, v) -> (k -> s -> Maybe v)
lookupOf o k = fmap snd . findOf o ((== k) . fst)


previews :: Fold s a -> (a -> r) -> (s -> Maybe r)
previews o f = getFirst #. foldMapOf o (First #. Just . f)

preview :: Fold s a -> s -> Maybe a
preview o = previews o id

(^?) :: s -> Fold s a -> Maybe a
s ^? o = preview o s

infixl 8 ^?


newtype Failover s a = Failover { getFailover :: Fold s a }

instance Semigroup (Failover s a) where
  Failover a1 <> Failover a2 = Failover (folding (\ s -> Cons (\ cons nil -> maybe (foldrOf a2 cons nil s) (uncurry cons) (foldrOf a1 (\ a -> Just . (,) a . maybe nil (uncurry cons)) Nothing s))))

instance Monoid (Failover s a) where
  mempty = Failover ignored


newtype Union s a = Union { getUnion :: Fold s a }

instance Semigroup (Union s a) where
  Union a1 <> Union a2 = Union (rphantom . traversal (\ f s -> traverseOf_ a1 f s *> traverseOf_ a2 f s) . rphantom)

instance Monoid (Union s a) where
  mempty = Union ignored