-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Making composable programs with multithreading, events and distributed computing -- -- See http://github.com/agocorona/transient In this release -- distributed primitives have been moved to the transient-universe -- package, and web primitives have been moved to the ghcjs-hplay -- package. @package transient @version 0.4.2 -- | See http://github.com/agocorona/transient everithing in this -- module is exported in order to allow extensibility. module Transient.Internals data TransIO x Transient :: StateT EventF IO (Maybe x) -> TransIO x [runTrans] :: TransIO x -> StateT EventF IO (Maybe x) type SData = () type EventId = Int type TransientIO = TransIO data EventF EventF :: Effects -> Maybe SData -> TransIO a -> [b -> TransIO b] -> Map TypeRep SData -> Int -> ThreadId -> Bool -> Maybe EventF -> TVar [EventF] -> Maybe (IORef Int) -> EventF [meffects] :: EventF -> Effects [event] :: EventF -> Maybe SData [xcomp] :: EventF -> TransIO a [fcomp] :: EventF -> [b -> TransIO b] [mfData] :: EventF -> Map TypeRep SData [mfSequence] :: EventF -> Int [threadId] :: EventF -> ThreadId [freeTh] :: EventF -> Bool [parent] :: EventF -> Maybe EventF [children] :: EventF -> TVar [EventF] [maxThread] :: EventF -> Maybe (IORef Int) type Effects = forall a b c. TransIO a -> TransIO a -> (a -> TransIO b) -> StateIO (StateIO (Maybe c) -> StateIO (Maybe c), Maybe a) type StateIO = StateT EventF IO -- | run the transient computation with a blank state runTransient :: TransIO x -> IO (Maybe x, EventF) -- | run the transient computation with an state runTransState :: EventF -> TransIO x -> IO (Maybe x, EventF) -- | get the continuation context: closure, continuation, state, child -- threads etc getCont :: TransIO EventF -- | run the closure and the continuation using the state data of the -- calling thread runCont :: EventF -> StateIO (Maybe a) -- | run the closure and the continuation using his own state data runCont' :: EventF -> IO (Maybe a, EventF) -- | warning: radiactive untyped stuff. handle with care getContinuations :: StateIO [a -> TransIO b] -- | compose a list of continuations compose :: (Alternative f, Monad f) => [t -> f t] -> t -> f a -- | run the closure (the x in 'x >>= f') of the current -- bind operation. runClosure :: EventF -> StateIO (Maybe a) -- | run the continuation (the f in 'x >>= f') of the -- current bind operation runContinuation :: EventF -> a -> StateIO (Maybe b) setContinuation :: TransIO a -> (a -> TransIO b) -> [c -> TransIO c] -> StateIO () -- | run a chain of continuations. It is up to the programmer to assure by -- construction that each continuation type-check with the next, that the -- parameter type match the input of the first continuation. Normally -- this makes sense if it stop the current flow with stop after -- the invocation runContinuations :: [a -> TransIO b] -> c -> TransIO d restoreStack :: MonadState EventF m => [b -> TransIO b] -> m () readWithErr :: (Typeable * a, Read a) => [Char] -> IO [(a, String)] readsPrec' :: (Typeable * a, Read a) => t -> [Char] -> [(a, String)] -- | dynamic serializable data for logging data IDynamic IDyns :: String -> IDynamic IDynamic :: a -> IDynamic type Recover = Bool type CurrentPointer = [LogElem] type LogEntries = [LogElem] data LogElem Wait :: LogElem Exec :: LogElem Var :: IDynamic -> LogElem data Log Log :: Recover -> CurrentPointer -> LogEntries -> Log data RemoteStatus WasRemote :: RemoteStatus WasParallel :: RemoteStatus NoRemote :: RemoteStatus -- | a sinonym of empty that can be used in a monadic expression. it stop -- the computation and execute the next alternative computation (composed -- with <|>) stop :: Alternative m => m stop class AdditionalOperators m where atEnd' = (<**) atEnd = (<***) -- | executes the second operand even if the frist return empty. A normal -- imperative (monadic) sequence uses the operator (>>) which in -- the Transient monad does not execute the next operand if the previous -- one return empty. (**>) :: AdditionalOperators m => m a -> m b -> m b -- | forces the execution of the second operand even if the first stop. It -- does not execute the second operand as result of internal events -- occuring in the first operand. Return the first result (<**) :: AdditionalOperators m => m a -> m b -> m a atEnd' :: AdditionalOperators m => m a -> m b -> m a -- | forces the execution of the second operand even if the first stop. -- Return the first result. The second operand is executed also when -- internal events happens in the first operand and it returns something (<***) :: AdditionalOperators m => m a -> m b -> m a atEnd :: AdditionalOperators m => m a -> m b -> m a -- | when the first operand is an asynchronous operation, the second -- operand is executed once (one single time) when the first completes -- his first asyncronous operation. -- -- This is useful for spawning asynchronous or distributed tasks that are -- singletons and that should start when the first one is set up. -- -- for example a streaming where the event receivers are acivated before -- the senders. (<|) :: TransIO a -> TransIO b -> TransIO a -- | set the current closure and continuation for the current statement setEventCont :: TransIO a -> (a -> TransIO b) -> StateIO EventF -- | reset the closure and continuation. remove inner binds than the -- previous computations may have stacked in the list of continuations. -- resetEventCont :: Maybe a -> EventF -> StateIO (TransIO b -> -- TransIO b) resetEventCont :: MonadState EventF m => Maybe t1 -> t -> m (a -> a) tailsafe :: [t] -> [t] baseEffects :: Effects waitQSemB :: (Ord a, Num a) => IORef a -> IO Bool signalQSemB :: Num a => IORef a -> IO () -- | set the maximun number of threads for a procedure. It is useful to -- limit the parallelization of transient code that uses parallel -- spawn and waitEvents threads :: Int -> TransIO a -> TransIO a -- | delete all the previous childs generated by the expression taken as -- parameter and continue execution of the current thread. oneThread :: TransIO a -> TransientIO a showThreads :: TransIO empty -- | add n threads to the limit of threads. If there is no limit, it set it addThreads' :: Int -> TransIO () -- | assure that at least there are n threads available addThreads :: Int -> TransIO () -- | The threads generated in the process passed as parameter will not be -- killed by `kill*` primitives freeThreads :: TransIO a -> TransIO a -- | The threads will be killed when the parent thread dies. That is the -- default. This can be invoked to revert the effect of -- freeThreads hookedThreads :: TransIO a -> TransIO a -- | kill all the child threads of the current thread killChilds :: TransientIO () -- | Get the state data for the desired type if there is any. getData :: (MonadState EventF m, Typeable a) => m (Maybe a) -- | getData specialized for the Transient monad. if Nothing, the monadic -- computation does not continue. -- -- If there is no such data, getSData silently stop the -- computation. That may or may not be the desired behaviour. To make -- sure that this does not get unnoticed, use this construction: -- --
--   getSData <|> error "no data"
--   
-- -- To have the same semantics and guarantees than get, use a -- default value: -- --
--   getInt= getSData <|> return (0 :: Int)
--   
-- -- The default value (0 in this case) has the same role than the initial -- value in a state monad. The difference is that you can define as many -- get as you need for all your data types. -- -- To distingish two data with the same types, use newtype definitions. getSData :: Typeable a => TransIO a -- | set session data for this type. retrieved with getData or getSData -- Note that this is data in a state monad, that means that the update -- only affect downstream in the monad execution. it is not a global -- state neither a per user or per thread state it is a monadic state -- like the one of a state monad. setData :: (MonadState EventF m, Typeable a) => a -> m () delData :: (MonadState EventF m, Typeable a) => a -> m () -- | generator of identifiers that are unique withing the current monadic -- sequence They are not unique in the whole program. genId :: MonadState EventF m => m Int getPrevId :: MonadState EventF m => m Int -- | async calls data StreamData a SMore :: a -> StreamData a SLast :: a -> StreamData a SDone :: StreamData a SError :: SomeException -> StreamData a -- | variant of parallel that repeatedly executes the IO computation -- and kill the previously created childs -- -- It is useful in single threaded problems where each event discard the -- computations spawned by previous events waitEvents :: IO b -> TransIO b waitEvents' :: IO b -> TransIO b -- | variant of parallel that execute the IO computation once, and -- kill the previous child threads async :: IO b -> TransIO b -- | variant that spawn free threads. Since there is no thread control, -- this is faster spawn :: IO b -> TransIO b -- | return empty to the current thread, in new thread, execute the IO -- action, this IO action modify an internal buffer. then, executes the -- closure where parallel is located In this new execution, since -- the buffer is filled, parallel return the content of this -- buffer. Then it launch the continuation after it with this new value -- returned by the closure. -- -- If the maximum number of threads, set with threads has been -- reached parallel perform the work sequentially, in the current -- thread. So parallel means that 'it can be parallelized if there -- are thread available' -- -- if there is a limitation of threads, when a thread finish, the counter -- of threads available is increased so another parallel can make -- use of it. -- -- The behaviour of parallel depend on StreamData; If -- SMore, parallel will excute again the IO action. with -- SLast, SDone and SError, parallel will not -- repeat the IO action anymore. parallel :: IO (StreamData b) -> TransIO (StreamData b) loop :: EventF -> IO (StreamData t) -> IO () forkFinally1 :: IO a -> (Either SomeException a -> IO ()) -> IO ThreadId free :: ThreadId -> EventF -> IO (Maybe EventF) hangThread :: EventF -> EventF -> IO () -- | kill all the child threads associated with the continuation context killChildren :: TVar [EventF] -> IO () type EventSetter eventdata response = (eventdata -> IO response) -> IO () type ToReturn response = IO response -- | deinvert an event handler. -- -- The first parameter is the setter of the event handler to be -- deinverted. Usually it is the primitive provided by a framework to set -- an event handler -- -- the second parameter is the value to return to the event handler. -- Usually it is `return()` -- -- it configures the event handler by calling the setter of the event -- handler with the current continuation react :: Typeable eventdata => EventSetter eventdata response -> ToReturn response -> TransIO eventdata getLineRef :: TVar (Maybe a) roption :: MVar [t] -- | install a event receiver that wait for a string and trigger the -- continuation when this string arrives. option :: (Typeable b, Show b, Read b, Eq b) => b -> String -> TransIO b -- | validates an input entered in the keyboard in non blocking mode. non -- blocking means that the user can enter also anything else to activate -- other option unlike option, wich watch continuously, input only -- wait for one valid response input :: (Typeable a, Read a, Show a) => (a -> Bool) -> String -> TransIO a -- | non blocking getLine with a validator getLine' :: (Typeable * a, Read a) => (a -> Bool) -> IO a reads1 :: (Typeable * a, Read a) => [Char] -> [(a, String)] inputLoop :: IO b processLine :: String -> IO () rexit :: MVar a -- | wait for the execution of exit and return the result stay :: IO b -- | keep the main thread running, initiate the non blocking keyboard input -- and execute the transient computation. -- -- It also read a slash-separated list of string that are read by -- option and input as if they were entered by the keyboard -- --
--   foo  -p  options/to/be/read/by/option/and/input
--   
keep :: TransIO a -> IO a -- | same than keepbut do not initiate the asynchronous keyboard -- input. Useful for debugging keep' :: TransIO a -> IO a -- | force the finalization of the main thread and thus, all the Transient -- block (and the application if there is no more code) exit :: a -> TransIO a exit' :: MonadIO m => a -> m () -- | alternative operator for maybe values. Used in infix mode onNothing :: Monad m => m (Maybe b) -> m b -> m b instance GHC.Read.Read a => GHC.Read.Read (Transient.Internals.StreamData a) instance GHC.Show.Show a => GHC.Show.Show (Transient.Internals.StreamData a) instance GHC.Show.Show Transient.Internals.RemoteStatus instance GHC.Classes.Eq Transient.Internals.RemoteStatus instance GHC.Show.Show Transient.Internals.LogElem instance GHC.Read.Read Transient.Internals.LogElem instance Control.Monad.State.Class.MonadState Transient.Internals.EventF Transient.Internals.TransIO instance GHC.Base.Functor Transient.Internals.TransIO instance GHC.Base.Applicative Transient.Internals.TransIO instance GHC.Show.Show Transient.Internals.IDynamic instance GHC.Read.Read Transient.Internals.IDynamic instance GHC.Base.Alternative Transient.Internals.TransIO instance GHC.Base.MonadPlus Transient.Internals.TransIO instance Transient.Internals.AdditionalOperators Transient.Internals.TransIO instance GHC.Base.Monoid a => GHC.Base.Monoid (Transient.Internals.TransIO a) instance GHC.Base.Monad Transient.Internals.TransIO instance Control.Monad.IO.Class.MonadIO Transient.Internals.TransIO instance GHC.Read.Read GHC.Exception.SomeException -- | See http://github.com/agocorona/transient module Transient.Base data TransIO x Transient :: StateT EventF IO (Maybe x) -> TransIO x [runTrans] :: TransIO x -> StateT EventF IO (Maybe x) type TransientIO = TransIO -- | keep the main thread running, initiate the non blocking keyboard input -- and execute the transient computation. -- -- It also read a slash-separated list of string that are read by -- option and input as if they were entered by the keyboard -- --
--   foo  -p  options/to/be/read/by/option/and/input
--   
keep :: TransIO a -> IO a -- | same than keepbut do not initiate the asynchronous keyboard -- input. Useful for debugging keep' :: TransIO a -> IO a -- | a sinonym of empty that can be used in a monadic expression. it stop -- the computation and execute the next alternative computation (composed -- with <|>) stop :: Alternative m => m stop -- | install a event receiver that wait for a string and trigger the -- continuation when this string arrives. option :: (Typeable b, Show b, Read b, Eq b) => b -> String -> TransIO b -- | validates an input entered in the keyboard in non blocking mode. non -- blocking means that the user can enter also anything else to activate -- other option unlike option, wich watch continuously, input only -- wait for one valid response input :: (Typeable a, Read a, Show a) => (a -> Bool) -> String -> TransIO a -- | force the finalization of the main thread and thus, all the Transient -- block (and the application if there is no more code) exit :: a -> TransIO a -- | variant of parallel that execute the IO computation once, and -- kill the previous child threads async :: IO b -> TransIO b -- | variant of parallel that repeatedly executes the IO computation -- and kill the previously created childs -- -- It is useful in single threaded problems where each event discard the -- computations spawned by previous events waitEvents :: IO b -> TransIO b -- | variant that spawn free threads. Since there is no thread control, -- this is faster spawn :: IO b -> TransIO b -- | return empty to the current thread, in new thread, execute the IO -- action, this IO action modify an internal buffer. then, executes the -- closure where parallel is located In this new execution, since -- the buffer is filled, parallel return the content of this -- buffer. Then it launch the continuation after it with this new value -- returned by the closure. -- -- If the maximum number of threads, set with threads has been -- reached parallel perform the work sequentially, in the current -- thread. So parallel means that 'it can be parallelized if there -- are thread available' -- -- if there is a limitation of threads, when a thread finish, the counter -- of threads available is increased so another parallel can make -- use of it. -- -- The behaviour of parallel depend on StreamData; If -- SMore, parallel will excute again the IO action. with -- SLast, SDone and SError, parallel will not -- repeat the IO action anymore. parallel :: IO (StreamData b) -> TransIO (StreamData b) -- | deinvert an event handler. -- -- The first parameter is the setter of the event handler to be -- deinverted. Usually it is the primitive provided by a framework to set -- an event handler -- -- the second parameter is the value to return to the event handler. -- Usually it is `return()` -- -- it configures the event handler by calling the setter of the event -- handler with the current continuation react :: Typeable eventdata => EventSetter eventdata response -> ToReturn response -> TransIO eventdata -- | set session data for this type. retrieved with getData or getSData -- Note that this is data in a state monad, that means that the update -- only affect downstream in the monad execution. it is not a global -- state neither a per user or per thread state it is a monadic state -- like the one of a state monad. setData :: (MonadState EventF m, Typeable a) => a -> m () -- | Get the state data for the desired type if there is any. getData :: (MonadState EventF m, Typeable a) => m (Maybe a) -- | getData specialized for the Transient monad. if Nothing, the monadic -- computation does not continue. -- -- If there is no such data, getSData silently stop the -- computation. That may or may not be the desired behaviour. To make -- sure that this does not get unnoticed, use this construction: -- --
--   getSData <|> error "no data"
--   
-- -- To have the same semantics and guarantees than get, use a -- default value: -- --
--   getInt= getSData <|> return (0 :: Int)
--   
-- -- The default value (0 in this case) has the same role than the initial -- value in a state monad. The difference is that you can define as many -- get as you need for all your data types. -- -- To distingish two data with the same types, use newtype definitions. getSData :: Typeable a => TransIO a delData :: (MonadState EventF m, Typeable a) => a -> m () -- | set the maximun number of threads for a procedure. It is useful to -- limit the parallelization of transient code that uses parallel -- spawn and waitEvents threads :: Int -> TransIO a -> TransIO a -- | assure that at least there are n threads available addThreads :: Int -> TransIO () -- | The threads generated in the process passed as parameter will not be -- killed by `kill*` primitives freeThreads :: TransIO a -> TransIO a -- | The threads will be killed when the parent thread dies. That is the -- default. This can be invoked to revert the effect of -- freeThreads hookedThreads :: TransIO a -> TransIO a -- | delete all the previous childs generated by the expression taken as -- parameter and continue execution of the current thread. oneThread :: TransIO a -> TransientIO a -- | kill all the child threads of the current thread killChilds :: TransientIO () -- | executes the second operand even if the frist return empty. A normal -- imperative (monadic) sequence uses the operator (>>) which in -- the Transient monad does not execute the next operand if the previous -- one return empty. (**>) :: AdditionalOperators m => m a -> m b -> m b -- | forces the execution of the second operand even if the first stop. It -- does not execute the second operand as result of internal events -- occuring in the first operand. Return the first result (<**) :: AdditionalOperators m => m a -> m b -> m a -- | forces the execution of the second operand even if the first stop. -- Return the first result. The second operand is executed also when -- internal events happens in the first operand and it returns something (<***) :: AdditionalOperators m => m a -> m b -> m a -- | when the first operand is an asynchronous operation, the second -- operand is executed once (one single time) when the first completes -- his first asyncronous operation. -- -- This is useful for spawning asynchronous or distributed tasks that are -- singletons and that should start when the first one is set up. -- -- for example a streaming where the event receivers are acivated before -- the senders. (<|) :: TransIO a -> TransIO b -> TransIO a -- | async calls data StreamData a SMore :: a -> StreamData a SLast :: a -> StreamData a SDone :: StreamData a SError :: SomeException -> StreamData a -- | generator of identifiers that are unique withing the current monadic -- sequence They are not unique in the whole program. genId :: MonadState EventF m => m Int module Transient.EVars data EVar a EVar :: (TChan (StreamData a)) -> EVar a -- | creates an EVar. -- -- Evars are event vars. writeEVar trigger the execution of all -- the continuations associated to the readEVar of this variable -- (the code that is after them). -- -- It is like the publish-subscribe pattern but without inversion of -- control, since a readEVar can be inserted at any place in the -- Transient flow. -- -- EVars are created upstream and can be used to communicate two -- sub-threads of the monad. Following the Transient philosophy they do -- not block his own thread if used with alternative operators, unlike -- the IORefs and TVars. And unlike STM vars, that are composable, they -- wait for their respective events, while TVars execute the whole -- expression when any variable is modified. -- -- The execution continues after the writeEVar when all subscribers have -- been executed. -- -- Now the continuations are executed in parallel. -- -- see -- https://www.fpcomplete.com/user/agocorona/publish-subscribe-variables-transient-effects-v newEVar :: TransIO (EVar a) -- | delete al the subscriptions for an evar. cleanEVar :: EVar a -> TransIO () -- | read the EVar. It only succeed when the EVar is being updated The -- continuation gets registered to be executed whenever the variable is -- updated. -- -- if readEVar is re-executed in any kind of loop, since each -- continuation is different, this will register again. The effect is -- that the continuation will be executed multiple times To avoid -- multiple registrations, use cleanEVar readEVar :: EVar a -> TransIO a -- | update the EVar and execute all readEVar blocks with "last in-first -- out" priority writeEVar :: MonadIO m => EVar t -> t -> m () -- | write the EVar and drop all the readEVar handlers. -- -- It is like a combination of writeEVar and cleanEVar lastWriteEVar :: MonadIO m => EVar t -> t -> m () type FinishReason = Maybe SomeException data Finish Finish :: (EVar FinishReason) -> Finish -- | initialize the event variable for finalization. all the following -- computations in different threads will share it it also isolate this -- event from other branches that may have his own finish variable initFinish :: TransIO Finish -- | set a computation to be called when the finish event happens onFinish :: (FinishReason -> TransIO ()) -> TransIO () -- | trigger the event, so this closes all the resources finish :: FinishReason -> TransIO () -- | deregister all the finalization actions. A initFinish is needed to -- register actions again unFinish :: TransIO () -- | kill all the processes generated by the parameter when finish event -- occurs killOnFinish :: TransIO b -> TransIO b -- | trigger finish when the stream data return SDone checkFinalize :: StreamData a -> TransIO a -- | see -- https://www.fpcomplete.com/user/agocorona/beautiful-parallel-non-determinism-transient-effects-iii module Transient.Indeterminism -- | slurp a list of values and process them in parallel . To limit the -- number of processing threads, use threads choose :: Show a => [a] -> TransIO a -- | alternative definition with more parallelism, as the composition of n -- async sentences choose' :: [a] -> TransIO a -- | execute a process and get at least the first n solutions (they could -- be more). if the process end without finding the number of solutions -- requested, it return the found ones if he find the number of solutions -- requested, it kill the non-free threads of the process and return It -- works monitoring the solutions found and the number of active threads. -- If the first parameter is 0, collect will return all the results collect :: Int -> TransIO a -> TransIO [a] -- | search also between two time intervals. If the first interval has -- passed and there is no result, it stops. After the second interval, it -- stop unconditionally and return the current results. It also stops as -- soon as there are enough results specified in the first parameter. collect' :: Int -> NominalDiffTime -> NominalDiffTime -> TransIO a -> TransIO [a] -- | group the output of a possible multithreaded process in groups of n -- elements. group :: Int -> TransIO a -> TransIO [a] -- | group result for a time interval, measured with diffUTCTime groupByTime :: Integer -> TransIO a -> TransIO [a] module Transient.Logged class (Show a, Read a, Typeable a) => Loggable a fromIDyn :: (Read a, Show a, Typeable a) => IDynamic -> a toIDyn :: (Typeable * a, Show a, Read a) => a -> IDynamic -- | write the result of the computation in the log and return it. but if -- there is data in the internal log, it read the data from the log and -- do not execute the computation. -- -- It accept nested step's. The effect is that if the outer step is -- executed completely the log of the inner steps are erased. If it is -- not the case, the inner steps are logged this reduce the log of large -- computations to the minimum. That is a feature not present in the -- package Workflow. -- --
--   r <- logged $ do
--           logged this :: TransIO ()
--           logged that :: TransIO ()
--           logged thatOther
--   liftIO $ print r
--   
-- -- when print is executed, the log is just the value of r. -- -- but at the thatOther execution the log is: [Exec,(), ()] logged :: Loggable a => TransientIO a -> TransientIO a instance (GHC.Show.Show a, GHC.Read.Read a, Data.Typeable.Internal.Typeable a) => Transient.Logged.Loggable a module Transient.Stream.Resource -- | slurp input from a file a line at a time. It creates as much threads -- as possible. to allow single threaded processing, use it with `threads -- 0` sourceFile :: String -> TransIO String -- | Stream the input to a file sinkFile :: TransIO String -> String -> TransIO () -- | is the general operation for processing a streamed input, with opening -- resources before processing and closing them when finish is called. -- The process statements suscribe to the Finish EVar. -- -- When this variable is updated, the close procedure is called. -- -- When the processing return SDone or SError, the -- Finish variable is updated so all the subscribed code, that -- close the resources, is executed. process :: TransIO a -> IO handle -> (handle -> FinishReason -> IO ()) -> (handle -> a -> TransIO (StreamData b)) -> TransIO b -- | initialize the event variable for finalization. all the following -- computations in different threads will share it it also isolate this -- event from other branches that may have his own finish variable initFinish :: TransIO Finish -- | trigger the event, so this closes all the resources finish :: FinishReason -> TransIO () -- | deregister all the finalization actions. A initFinish is needed to -- register actions again unFinish :: TransIO () -- | set a computation to be called when the finish event happens onFinish :: (FinishReason -> TransIO ()) -> TransIO () -- | -- https://www.fpcomplete.com/user/agocorona/the-hardworking-programmer-ii-practical-backtracking-to-undo-actions module Transient.Backtrack -- | register an action that will be executed when backtracking registerUndo :: TransientIO a -> TransientIO a -- | the secod parameter will be executed when backtracking onUndo :: TransientIO a -> TransientIO a -> TransientIO a -- | execute backtracking. It execute the registered actions in reverse -- order. -- -- If the backtracking flag is changed the flow proceed forward from that -- point on. -- -- If the backtrack stack is finished or undoCut executed, undo -- will stop. undo :: TransientIO a -- | restart the flow forward from this point on retry :: TransIO () -- | assures that backtracking will not go further back undoCut :: TransientIO ()