{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE Trustworthy #-}
module Data.Fold.Class
  ( Scan(..)
  , Folding(..)
  , beneath
  ) where

import Control.Lens
import Data.Fold.Internal
import Data.Profunctor.Unsafe

--
-- $setup
-- >>> import Control.Lens
-- >>> import Data.Fold

class Choice p => Scan p where
  prefix1 :: a -> p a b -> p a b
  default prefix1 :: Folding p => a -> p a b -> p a b
  prefix1 = An a -> p a b -> p a b
forall (p :: * -> * -> *) (t :: * -> *) a b.
(Folding p, Foldable t) =>
t a -> p a b -> p a b
prefix (An a -> p a b -> p a b) -> (a -> An a) -> a -> p a b -> p a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> An a
forall a. a -> An a
An
  {-# INLINE prefix1 #-}

  postfix1 :: p a b -> a -> p a b
  default postfix1 :: Folding p => p a b -> a -> p a b
  postfix1 p a b
p = p a b -> An a -> p a b
forall (p :: * -> * -> *) (t :: * -> *) a b.
(Folding p, Foldable t) =>
p a b -> t a -> p a b
postfix p a b
p (An a -> p a b) -> (a -> An a) -> a -> p a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> An a
forall a. a -> An a
An
  {-# INLINE postfix1 #-}

  -- | Apply a 'Folding' to a single element of input
  run1 :: a -> p a b -> b
  default run1 :: Folding p => a -> p a b -> b
  run1 = An a -> p a b -> b
forall (p :: * -> * -> *) (t :: * -> *) a b.
(Folding p, Foldable t) =>
t a -> p a b -> b
run (An a -> p a b -> b) -> (a -> An a) -> a -> p a b -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> An a
forall a. a -> An a
An
  {-# INLINE run1 #-}

  interspersing :: a -> p a b -> p a b

class Scan p => Folding p where
  -- | Partially apply a 'Folding' to some initial input on the left.
  --
  prefix :: Foldable t => t a -> p a b -> p a b
  prefix = Fold (t a) a -> t a -> p a b -> p a b
forall (p :: * -> * -> *) s a b.
Folding p =>
Fold s a -> s -> p a b -> p a b
prefixOf Fold (t a) a
forall (f :: * -> *) a. Foldable f => IndexedFold Int (f a) a
folded
  {-# INLINE prefix #-}

  prefixOf :: Fold s a -> s -> p a b -> p a b

  postfix :: Foldable t => p a b -> t a -> p a b
  postfix = Fold (t a) a -> p a b -> t a -> p a b
forall (p :: * -> * -> *) s a b.
Folding p =>
Fold s a -> p a b -> s -> p a b
postfixOf Fold (t a) a
forall (f :: * -> *) a. Foldable f => IndexedFold Int (f a) a
folded
  {-# INLINE postfix #-}

  postfixOf :: Fold s a -> p a b -> s -> p a b

  -- | Apply a 'Folding' to a container full of input:
  --
  -- >>> run ["hello","world"] $ L id (++) []
  -- "helloworld"
  --
  -- >>> run [1,2,3] $ L id (+) 0
  -- 6
  run :: Foldable t => t a -> p a b -> b
  run = Fold (t a) a -> t a -> p a b -> b
forall (p :: * -> * -> *) s a b.
Folding p =>
Fold s a -> s -> p a b -> b
runOf Fold (t a) a
forall (f :: * -> *) a. Foldable f => IndexedFold Int (f a) a
folded
  {-# INLINE run #-}

  runOf :: Fold s a -> s -> p a b -> b

  filtering :: (a -> Bool) -> p a b -> p a b

-- | Lift a 'Folding' into a 'Prism'.
--
-- This acts like a generalized notion of \"costrength\",
-- when applied to a 'Folding', causing it to return the
-- left-most value that fails to match the Prism, or the
-- result of accumulating rewrapped in the 'Prism' if
-- everything matches.
--
-- >>> run [Left 1, Left 2, Left 3] $ beneath _Left $ R id (+) 0
-- Left 6
--
-- >>> run [Left 1, Right 2, Right 3] $ beneath _Left $ R id (+) 0
-- Right 2
--
-- @
-- beneath :: Prism s t a b -> p a b -> p s t
-- beneath :: Iso s t a b   -> p a b -> p s t
-- @
beneath :: Profunctor p => Optic p Identity s t a b -> p a b -> p s t
beneath :: Optic p Identity s t a b -> p a b -> p s t
beneath Optic p Identity s t a b
l p a b
f = Identity t -> t
forall a. Identity a -> a
runIdentity (Identity t -> t) -> p s (Identity t) -> p s t
forall (p :: * -> * -> *) a b c (q :: * -> * -> *).
(Profunctor p, Coercible c b) =>
q b c -> p a b -> p a c
#. Optic p Identity s t a b
l (b -> Identity b
forall a. a -> Identity a
Identity (b -> Identity b) -> p a b -> p a (Identity b)
forall (p :: * -> * -> *) a b c (q :: * -> * -> *).
(Profunctor p, Coercible c b) =>
q b c -> p a b -> p a c
#. p a b
f)