Status
Status is the completion signal that a Stage attaches to every Yield it returns.
As stages are composed into a pipeline via ~>, their individual statuses are merged into a single overall status
for the run.
Two things can be signalled:
- that the stage completed normally, with no special signal;
- that a unit of work has finished — either cleanly or with accumulated errors. This is passed to
Evolutionwhen selecting the next continuation stage, and intercepted by enclosing alterators such asLoopandRepeatas the cue to end the current cycle.
import h8io.stages.*
The Two Variants
Status.Success is the ordinary outcome. The stage did its work and the pipeline is free to move on.
Status.Complete signals that a unit of work has finished. When its errors sequence is empty this represents clean
completion; when errors is non-empty one or more errors were accumulated. The signal is passed to
Evolution.evolve, which may select a different continuation stage than it would for Success. Enclosing alterators
such as Loop and Repeat use it as the cue to stop looping and reset for the next cycle.
Two convenience values cover the common cases:
Status.complete— aCompletewith no errors, for signalling clean termination.Status.error(head, tail*)— aCompletewith one or more error values.
Combining Statuses
When a pipeline composes two stages with ~> and both run on the same input, their statuses are merged by combine.
The rule is simple: the result is whichever of the two is more severe.
Success is the identity element. Combining it with any other status leaves that status unchanged:
Status.Success.combine(Status.Success)
// res0: Status[Nothing] = Success
Status.Success.combine(Status.complete)
// res1: Status[Nothing] = Complete()
Status.Success.combine(Status.error("something went wrong"))
// res2: Status[String] = Complete("something went wrong")
Complete dominates Success. Two Complete values are merged by concatenating their error sequences,
preserving the left-to-right order of the stages in the pipeline:
Status.complete.combine(Status.Success)
// res3: Status[Nothing] = Complete()
Status.complete.combine(Status.complete)
// res4: Status[Nothing] = Complete()
Status.error("first").combine(Status.error("second"))
// res5: Status[String] = Complete("first", "second")
Together, combine and Success make Status[E] a monoid for any fixed E: combine is associative, and
Success is its identity element. The monoid is not commutative in general — when two Complete values with errors
are combined, the order of their error sequences is significant.
Accessing Errors
Status.Complete extends Iterable[E], so errors can be iterated directly with foreach or a for-comprehension.
errors holds the full sequence; toList returns them all in order:
val err = Status.error("disk full", "timeout", "connection reset")
// err: Status.Complete[String] = Complete(
// "disk full",
// "timeout",
// "connection reset"
// )
err.errors
// res6: Seq[String] = ArraySeq("disk full", "timeout", "connection reset")
err.toList
// res7: List[String] = List("disk full", "timeout", "connection reset")
for (message <- err) println(message)
// disk full
// timeout
// connection reset
Status.complete has no errors and is therefore empty:
Status.complete.isEmpty
// res9: Boolean = true
Status.complete.toList
// res10: List[Nothing] = List()
Transforming Error Values
map applies a function to every error value contained in a Status.Complete, leaving Status.Success and
error-free Complete values unchanged. This is useful when a pipeline boundary needs to change the error
representation:
val numeric = Status.error("42", "7", "100").map(_.toInt)
// numeric: Status.Complete[Int] = Complete(42, 7, 100)
numeric.toList
// res11: List[Int] = List(42, 7, 100)
Because Status.Success and Status.complete carry no error values, map returns them unchanged:
(Status.Success: Status[String]).map(identity)
// res12: Status[String] = Success
(Status.complete: Status[String]).map(identity)
// res13: Status[String] = Complete()