packages feed

supply-next-0.0.0.0: test-supply-next/Main.hs

module Main (main) where

import Essentials

import Control.Monad (replicateM)
import Control.Monad.Trans.Writer.CPS (execWriter, tell, runWriter, Writer)
import Data.Function (on)
import Data.Monoid (Sum (..))
import Data.Sequence (Seq)
import Data.String (String)
import Next (each, ProducerPlus)
import Next.Stream (Stream (Stream))
import Prelude (Integer, Int, IO, (+), show)
import SupplyChain ((>->), (>-), Job)
import Test.Hspec (hspec, describe, it, shouldBe)

import qualified Data.Char as Char
import qualified Data.Sequence as Seq
import qualified Fold.Effectful as Fold
import qualified Next
import qualified Next.Stream as Stream
import qualified SupplyChain.Job as Job
import qualified Data.List as List

main :: IO ()
main = hspec $ describe "Next" do

    describe "Pipe" do

        let write :: Next.ConsumerPlus up (Writer (Seq item)) item ()
            write = Next.foldEffect (Fold.effect (Seq.singleton >>> tell))

        it "group" do
            let input = "Hrmm..."; grouped = [(1, 'H'), (1, 'r'), (2, 'm'), (3, '.')]
                job = each input >-> Next.group >- write
            execWriter (Job.run job) `shouldBe` Seq.fromList grouped

        it "takeWhile" do
            let input = "PORKchOp"; capitalPrefix = "PORK"
                job = each input >-> Next.takeWhile (pure . Char.isUpper) >- write
            execWriter (Job.run job) `shouldBe` Seq.fromList capitalPrefix

        describe "intersperse" do
            let xs, ys :: [Integer]; xs = [1, 2, 3]; ys = [1, 0, 2, 0, 3]

            it "intersperses items" do
                let job = each xs >-> Next.intersperse separator >- Next.toList
                    separator = pure 0
                runIdentity (Job.run job) `shouldBe` ys

            it "also intersperses effects" do
                let job = each xs >-> Next.intersperse separator >- Next.toList
                    separator = 0 <$ Job.perform (tell (Sum (1 :: Integer)))
                runWriter (Job.run job) `shouldBe` (ys, Sum 2)

    describe "Consumer" do

        let write :: Int -> Job up (Writer String) ()
            write x = Job.perform $ tell $ show x

            input = [2, 3, 4] :: [Int]

            producer :: ProducerPlus up (Writer String) Int
            producer = each input >-> Next.map \x -> write x $> x

        it "combines values and effects from the list" do
            let f = Fold.sum; job = producer >- Next.foldEffect f
            runWriter (Job.run job) `shouldBe` (List.sum input, "234")

        it "also incorporates effects from the fold" do
            let f = Fold.sum <* Fold.effect (\n -> replicateM n $ tell "-")
                job = producer >- Next.foldEffect f
            runWriter (Job.run job) `shouldBe` (List.sum input, "2--3---4----")

    describe "Stream" do

        let write :: forall a. a -> Stream (Writer (Seq a)) ()
            write = Stream . Next.effect . tell . Seq.singleton

        it "is a monad" do
            let a, b :: [Integer]; a = [10, 20, 30]; b = [1, 2, 3]
                stream = ((+) <$> Stream (each a) <*> Stream (each b)) >>= write
                job = Stream.producer stream >- Next.run
            execWriter (Job.run job) `shouldBe`
                Seq.fromList [11, 12, 13, 21, 22, 23, 31, 32, 33]

        describe "(<>)" do
            let a, b :: [Integer]; a = [10, 20, 30]; b = [1, 2, 3]
            it "pure" do
                let (===) = shouldBe `on` \s -> do
                        let job = Stream.producer s >- Next.toList
                        runIdentity $ Job.run job
                (Stream (each a) <> Stream (each b)) === Stream (each (a <> b))
            it "effectful" do
                let stream = (Stream (each a) <> Stream (each b)) >>= write
                    job = Stream.producer stream >- Next.run
                shouldBe @(Seq Integer) (execWriter $ Job.run job)
                    (Seq.fromList (a <> b))