module Next.Pipe.Examples
  (
    {- * Triviality -} id,
    {- * Predicates -} takeWhile, dropWhile,
    {- * Insertion -} cons, intersperse, beforeEach,
    {- * Mapping -} map,
    {- * Concatenation -} concat,
    {- * Concat + map -} concatMapJob, concatMapProducer,
    {- * Grouping -} group,
  )
  where

import Essentials hiding (id)

import Next.Interface (Next (..), Step (..), TerminableStream (..), next)
import Next.Pipe.Type (Pipe, PipePlus)
import Next.Producer.Examples (append, empty)
import Next.Producer.Type (Producer, ProducerPlus)
import Integer (Positive)
import Prelude ((+))
import SupplyChain (Job, Vendor (..), Referral (..))

import qualified SupplyChain.Alter as Alter
import qualified SupplyChain.Job as Job
import qualified SupplyChain.Vendor as Vendor

{-| Apply a function to each item in the stream -}
map :: forall item1 item2 action up.
    (item1 -> Job up action item2)
        -- ^ For each input item, this job produces an output item
    -> PipePlus up action item1 item2
map :: forall item1 item2 (action :: * -> *) (up :: * -> *).
(item1 -> Job up action item2) -> PipePlus up action item1 item2
map item1 -> Job up action item2
f = Vendor up (Next item2) action
go
  where
    go :: Vendor up (Next item2) action
go = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next item2 product
Next -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order forall item (interface :: * -> *).
TerminableStream item interface =>
interface (Step item)
next forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Step item1
End -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
        Item item1
a -> item1 -> Job up action item2
f item1
a forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \item2
b -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item2
b) Vendor up (Next item2) action
go

{-| Applies the function to each result obtained from upstream,
    and yields each result from the list to the downstream -}
concatMapJob :: forall item1 item2 action up.
    (item1 -> Job up action [item2])
        -- ^ For each input item, this job produces any number of output items
    -> PipePlus up action item1 item2
concatMapJob :: forall item1 item2 (action :: * -> *) (up :: * -> *).
(item1 -> Job up action [item2]) -> PipePlus up action item1 item2
concatMapJob item1 -> Job up action [item2]
f = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next item2 product
Next -> TerminableStream item1 up =>
[item2]
-> Job up action (Referral up (Next item2) action (Step item2))
go []
  where
    go :: TerminableStream item1 up => [item2] -> Job up action
        (Referral up (Next item2) action (Step item2))
    go :: TerminableStream item1 up =>
[item2]
-> Job up action (Referral up (Next item2) action (Step item2))
go = \case
        item2
b : [item2]
bs -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item2
b) forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next item2 product
Next -> TerminableStream item1 up =>
[item2]
-> Job up action (Referral up (Next item2) action (Step item2))
go [item2]
bs
        [] -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order forall item (interface :: * -> *).
TerminableStream item interface =>
interface (Step item)
next forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Step item1
End -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
            Item item1
a -> item1 -> Job up action [item2]
f item1
a forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= TerminableStream item1 up =>
[item2]
-> Job up action (Referral up (Next item2) action (Step item2))
go

{-| Flattens a stream of lists -}
concat :: forall item action up. PipePlus up action [item] item
concat :: forall item (action :: * -> *) (up :: * -> *).
PipePlus up action [item] item
concat = forall item1 item2 (action :: * -> *) (up :: * -> *).
(item1 -> Job up action [item2]) -> PipePlus up action item1 item2
concatMapJob forall (f :: * -> *) a. Applicative f => a -> f a
pure

{-| Yields the longest prefix matching the predicate and discards the rest -}
takeWhile :: forall item action up.
    (item -> Job up action Bool)
        -- ^ True if this is the sort of thing we'd like to keep
    -> PipePlus up action item item
takeWhile :: forall item (action :: * -> *) (up :: * -> *).
(item -> Job up action Bool) -> PipePlus up action item item
takeWhile item -> Job up action Bool
ok = Vendor up (Next item) action
go
  where
    go :: Vendor up (Next item) action
go = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \r :: Next item product
r@Next item product
Next -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order (forall item (interface :: * -> *) result.
TerminableStream item interface =>
Next item result -> interface result
liftNext Next item product
r) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Item item
x -> item -> Job up action Bool
ok item
x forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
            Bool
True -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item
x) Vendor up (Next item) action
go
            Bool
False -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
        product
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty

{-| Discards the longest prefix matching the predicate and yields the rest -}
dropWhile :: forall item action up.
    (item -> Job up action Bool)
        -- ^ True if this is the sort of thing we'd like to get rid of
    -> PipePlus up action item item
dropWhile :: forall item (action :: * -> *) (up :: * -> *).
(item -> Job up action Bool) -> PipePlus up action item item
dropWhile item -> Job up action Bool
bad = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \r :: Next item product
r@Next item product
Next ->
    let
        go :: Job up action (Referral up (Next item) action (Step item))
        go :: Job up action (Referral up (Next item) action (Step item))
go = forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order (forall item (interface :: * -> *) result.
TerminableStream item interface =>
Next item result -> interface result
liftNext Next item product
r) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
          Item item
x -> item -> Job up action Bool
bad item
x forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
              Bool
True -> Job up action (Referral up (Next item) action (Step item))
go
              Bool
False -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item
x) forall (up :: * -> *) item (action :: * -> *).
PipePlus up action item item
id
          product
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
    in
        Job up action (Referral up (Next item) action (Step item))
go

{- | Does nothing at all -}
id :: forall up item action. PipePlus up action item item
id :: forall (up :: * -> *) item (action :: * -> *).
PipePlus up action item item
id = forall (down :: * -> *) (up :: * -> *) (action :: * -> *).
(forall x. down x -> up x) -> Vendor up down action
Vendor.map forall item (interface :: * -> *) result.
TerminableStream item interface =>
Next item result -> interface result
liftNext

{-| Removes consecutive duplicate items, and yields each item along
    with the size of the repetition

For example, @\"Hrmm..."@ groups into
@[(1, \'H'), (1, \'r'), (2, \'m'), (3, \'.')]@ -}
group :: forall up item action. Eq item => PipePlus up action item (Positive, item)
group :: forall (up :: * -> *) item (action :: * -> *).
Eq item =>
PipePlus up action item (Positive, item)
group =
    forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next (Positive, item) product
Next -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order forall item (interface :: * -> *).
TerminableStream item interface =>
interface (Step item)
next forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Step item
End -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
        Item item
x -> TerminableStream item up =>
item
-> Job
     up
     action
     (Referral
        up (Next (Positive, item)) action (Step (Positive, item)))
start item
x

  where
    start :: TerminableStream item up => item
        -> Job up action (Referral up (Next (Positive, item)) action (Step (Positive, item)))
    start :: TerminableStream item up =>
item
-> Job
     up
     action
     (Referral
        up (Next (Positive, item)) action (Step (Positive, item)))
start = TerminableStream item up =>
Positive
-> item
-> Job
     up
     action
     (Referral
        up (Next (Positive, item)) action (Step (Positive, item)))
go Positive
1

    go :: TerminableStream item up => Positive -> item
        -> Job up action (Referral up (Next (Positive, item)) action (Step (Positive, item)))
    go :: TerminableStream item up =>
Positive
-> item
-> Job
     up
     action
     (Referral
        up (Next (Positive, item)) action (Step (Positive, item)))
go Positive
n item
x = forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order forall item (interface :: * -> *).
TerminableStream item interface =>
interface (Step item)
next forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Step item
End -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item (Positive
n, item
x)) forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
        Item item
y ->
          if item
y forall a. Eq a => a -> a -> Bool
== item
x
          then TerminableStream item up =>
Positive
-> item
-> Job
     up
     action
     (Referral
        up (Next (Positive, item)) action (Step (Positive, item)))
go (Positive
n forall a. Num a => a -> a -> a
+ Positive
1) item
x
          else forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item (Positive
n, item
x)) forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next (Positive, item) product
Next -> TerminableStream item up =>
item
-> Job
     up
     action
     (Referral
        up (Next (Positive, item)) action (Step (Positive, item)))
start item
y

{-| Add one item to the beginning of a stream -}
cons :: forall item action up.
    Job up action item
        -- ^ This job produces an item to add to the front of the list
    -> PipePlus up action item item
cons :: forall item (action :: * -> *) (up :: * -> *).
Job up action item -> PipePlus up action item item
cons Job up action item
head = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next item product
Next -> Job up action item
head forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \item
x -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item
x) forall (up :: * -> *) item (action :: * -> *).
PipePlus up action item item
id

{-| Add an item between each pair of items of a stream

The length of the stream is modified as @\\case{ 0 -> 0; n -> (2 * n) - 1 }@. -}
intersperse :: forall item action up.
    Job up action item
        -- ^ This job generates items that will be inserted in between
        --   the items of the original list
    -> PipePlus up action item item
intersperse :: forall item (action :: * -> *) (up :: * -> *).
Job up action item -> PipePlus up action item item
intersperse Job up action item
i = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \r :: Next item product
r@Next item product
Next -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order (forall item (interface :: * -> *) result.
TerminableStream item interface =>
Next item result -> interface result
liftNext Next item product
r) forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
    product
Step item
End -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
    Item item
x -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item
x) (forall item (action :: * -> *) (up :: * -> *).
Job up action item -> PipePlus up action item item
beforeEach Job up action item
i)

{-| Add an item before each item in a stream

The length of the stream is doubled. -}
beforeEach :: forall item action up.
    Job up action item
        -- ^ This job generates items that will be inserted before each
        --   of the items of the original list
    -> PipePlus up action item item
beforeEach :: forall item (action :: * -> *) (up :: * -> *).
Job up action item -> PipePlus up action item item
beforeEach Job up action item
i = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \r :: Next item product
r@Next item product
Next -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order (forall item (interface :: * -> *) result.
TerminableStream item interface =>
Next item result -> interface result
liftNext Next item product
r) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    product
Step item
End -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
    Item item
x -> Job up action item
i forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \item
i' -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item
i') forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next item product
Next ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral (forall item. item -> Step item
Item item
x) (forall item (action :: * -> *) (up :: * -> *).
Job up action item -> PipePlus up action item item
beforeEach Job up action item
i)

-- | Like 'concatMapJob', but the function gives a 'Producer' instead of a 'Job'
concatMapProducer :: forall item1 item2 action.
    (item1 -> Producer action item2)
        -- ^ For each item from the input list, this vendor generates
        --   any number of actions to yield in the resulting list
    -> Pipe action item1 item2
concatMapProducer :: forall item1 item2 (action :: * -> *).
(item1 -> Producer action item2) -> Pipe action item1 item2
concatMapProducer item1 -> Producer action item2
f = Vendor (Next item1) (Next item2) action
go
  where
    go :: Vendor (Next item1) (Next item2) action
go = forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
(forall product.
 down product -> Job up action (Referral up down action product))
-> Vendor up down action
Vendor \Next item2 product
Next -> forall (up :: * -> *) product (action :: * -> *).
up product -> Job up action product
Job.order forall item (interface :: * -> *).
TerminableStream item interface =>
interface (Step item)
next forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Step item1
End -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (up :: * -> *) (down :: * -> *) (action :: * -> *) product.
product -> Vendor up down action -> Referral up down action product
Referral forall item. Step item
End forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
empty
        Item item1
x -> forall (up :: * -> *) (down :: * -> *) (action :: * -> *).
Vendor up down action
-> forall product.
   down product -> Job up action (Referral up down action product)
handle (forall (up :: * -> *) item (action :: * -> *).
ProducerPlus up action item
-> ProducerPlus up action item -> ProducerPlus up action item
append Vendor (Next item1) (Next item2) action
v Vendor (Next item1) (Next item2) action
go) forall item result. (result ~ Step item) => Next item result
Next
          where
            v :: Vendor (Next item1) (Next item2) action
v = (item1 -> Producer action item2
f item1
x :: ProducerPlus (Const Void) action item2)
                forall a b. a -> (a -> b) -> b
& forall (up :: * -> *) (action :: * -> *) (up' :: * -> *)
       (action' :: * -> *) (down :: * -> *).
(forall x. Effect up action x -> Effect up' action' x)
-> Vendor up down action -> Vendor up' down action'
Alter.vendor' (forall {k} (up :: k -> *) (product :: k) (up' :: k -> *)
       (action :: k -> *).
(up product -> up' product)
-> Effect up action product -> Effect up' action product
Alter.request' forall a b. (a -> b) -> a -> b
$ \Const Void x
z -> case Const Void x
z of {})