Safe

Safe catches non-fatal exceptions thrown by the inner stage and converts them into Status.Complete error values, widening the error type from E to Either[Throwable, E].

Fatal exceptions (those not matched by scala.util.control.NonFatal) propagate normally. The evolution is mapped so that every continuation stage remains wrapped in Safe.

import h8io.stages.*
import h8io.stages.base.*
import h8io.stages.operators.*
object ParseIntUnsafe extends SAMStage[String, Int, Nothing] {
  override def apply(in: String): Yield[String, Int, Nothing] =
    Yield.Some(in.toInt, Status.Success, this)
}

val safe = Safe(ParseIntUnsafe)
// safe: Safe[String, Int, Nothing] = Safe(alterand = <function1>)
safe("42")
// res0: Yield[String, Int, Either[Throwable, Nothing]] = Some(
//   out = 42,
//   status = Success,
//   evolution = Mapped(
//     evolution = <function1>,
//     f = h8io.stages.operators.Safe$$Lambda$14388/0x00007f2c9264f640@6ace02bb
//   )
// )
safe("hello")
// res1: Yield[String, Int, Either[Throwable, Nothing]] = None(
//   status = Complete(
//     Left(value = java.lang.NumberFormatException: For input string: "hello")
//   ),
//   evolution = ConstEvolution(
//     stage = Safe(alterand = <function1>),
//     _dispose = h8io.stages.base.ConstEvolution$$$Lambda$14325/0x00007f2c92621d60@4bfbbb9
//   )
// )