{-| This module exports the 'Plan' Applicative. >>> :{ let example :: Plan String [Int] IO () () example = step "a" (step "b" (foretell [1] *> plan (threadDelay 1e6)) *> step "c" (foretell [2] *> plan (threadDelay 1e6))) *> step "d" (step "e" (foretell [3] *> plan (threadDelay 1e6)) *> step "f" (foretell [4] *> plan (threadDelay 1e6))) in bifoldMap id (foldMap Prelude.show) (getSteps example) :} "ab1c2de3f4" Some possible use cases: - Inspect the steps of an existing 'Plan' from @ghci@ using 'getSteps', 'toForest' and 'Data.Tree.drawForest', as a form of documentation. - If your script requires files that must be already present in the file system, use 'foretell' to annotate each 'Plan' action that requires a file, then get the global list of files using 'getSteps' and 'foldMap', and check that they all exist before running the 'Plan' with 'runPlan'. - Get progress updates for your script by declaring (possibly nested) steps with 'step', running the 'Plan' with 'runPlan', and providing a notification callback with 'onTick', probably using 'completedness','toForest' and 'Data.Tree.drawForest' to render the updates. - Run a 'Plan' with 'runPlan', use 'instants' an 'toForest' on the resulting 'Timeline' to get the durations of each step, then use 'zipSteps' on the same 'Plan' and run it again. Now whenever a step finishes we can know if it took more or less than in the previous execution. -} module Control.Plan ( -- * Constructing plans Plan ,plan ,planIO ,kplan ,kplanIO -- ** Declaring steps and annotations ,step ,skippable ,foretell -- * Analyzing plans ,getSteps ,Steps ,mandatoriness ,Mandatoriness(..) ,foldSteps -- * Adapting plans -- $adapting ,bimapSteps ,zoomSteps ,zipSteps ,hoistPlan -- * Running plans ,unliftPlan ,runPlan ,onTick ,Tick(..) ,completedness ,Context(..) ,Progress(..) ,Timeline ,instants ,foldTimeline -- ** Running arrow plans ,unliftKPlan ,runKPlan -- * The Sylvan typeclass ,Sylvan(..) -- * Re-exports ,Data.Bifunctor.bimap ,Data.Bifoldable.bifoldMap ,Data.Bitraversable.bitraverse ,Control.Comonad.extract -- $extract ,Streaming.hoist ,Streaming.Prelude.effects -- $effects ) where import Data.Bifunctor import Data.Bifoldable import Data.Bitraversable import Control.Comonad import Streaming import Streaming.Prelude import Control.Plan.Core {- $setup >>> :set -XNumDecimals >>> :set -XArrows >>> import Control.Applicative >>> import Control.Plan >>> import Control.Concurrent(threadDelay) -} {- $adapting Sometimes, we might need to mix 'Plan's for which step tags and annotations are of different types. These functions help with that. -} {- $extract Besides its usefulness with 'Timeline', 'extract' lets you get the head of a 'NonEmpty' or the second element of a tuple. -} {- $effects 'effects' lets you ignore all the update notifications while running a plan, when you are only interested in the final 'Timeline' and the result. -}