APIFail
Fail
The conventional failure effect.
A computation that can fail performs Fail error; attempt runs it and
turns its outcome into a Result. The module is not part of the prelude, so
a module that declares its own Fail keeps compiling; import
Fail exposing (Fail, attempt, fail) to use it.
Diagnostic
type Diagnostic error = { primary : error , trace : List Frame , suppressed : List Failure }Source
A typed failure with its original call stack and later cleanup failures.
Like Report, suppressed snapshots retain their payloads, including resources.
Show renders the fields when the primary error has a Show instance. For
stack lines in a human-readable report, use Trace.render on trace.
import Fail
diagnostic = Fail.Diagnostic { primary = 7, trace = [], suppressed = [] }
show diagnostic == "Diagnostic { primary = 7, trace = [], suppressed = [] }"
Fields
trace
trace : List FrameSource
The stack at the failure site, captured before unwinding and ordered
innermost first. Frame conventions are those of Trace.capture.
suppressed
suppressed : List FailureSource
Cleanup failures in the same order as Report.suppressed. Each has
its own optional stack, inspected with Failure.trace.
Instances:
Show error => Show (Diagnostic error)
Report
type Report error = { primary : error , suppressed : List Failure }Source
A failure together with the failures that cleanup raised after it.
Show renders its fields when the primary error has a Show instance;
suppressed failures show metadata without their opaque argument contents.
import Fail
report = Fail.Report { primary = "bad", suppressed = [] }
show report == "Report { primary = \"bad\", suppressed = [] }"
Fields
suppressed
suppressed : List FailureSource
Failures raised later by cleanup while the computation unwound,
as detached snapshots that Failure inspects.
Instances:
Show error => Show (Report error)
Fail
effect Fail errorSource
Failure with an error. Nested attempts handle only the failures raised
inside them.
attempt
attempt : (() ->{Fail a, e} b) ->{e} Result a bSource
Runs action, answering Ok with its value, or Err with the error it
failed with. Other effects pass through.
import Fail exposing (Fail, fail)
import Result exposing (Result(..))
checked : Int ->{Fail String} Int
checked n = if n > 0 then n else fail "not positive"
Fail.attempt { checked 2 } == Ok 2
Fail.attempt { checked 0 } == Err "not positive"
attemptDiagnostic
attemptDiagnostic : (() ->{Fail a, e} b) ->{e} Result (Diagnostic a) bSource
Like attemptReport, but captures the primary failure’s stack before
unwinding. Ok performs no stack capture. Only failures targeting this
handler capture: an inner ordinary attempt and unrelated abort handlers
do not. Cleanup failures targeting this handler capture their own stacks;
other suppressed failures may have no trace.
Propagation preserves the captured stack. Calling fail again, including
through fromResult, is a new failure occurrence: a plain Result does
not preserve diagnostic metadata. To keep a child task’s original trace,
return its diagnostic as ordinary data; existing Async helpers re-raise
errors in their caller. Panics and cancellation keep their existing behavior.
Capture happens even if the diagnostic is later discarded; Trace.render
renders the stored frames without another capture.
import Fail exposing (Fail, fail)
import Result
checked : Int ->{Fail String} Int
checked n = if n > 0 then n else fail "not positive"
Result.mapError { diagnostic -> diagnostic.primary } (Fail.attemptDiagnostic { checked 0 }) == Err "not positive"
Result.mapError { diagnostic -> diagnostic.primary } (Fail.attemptDiagnostic { checked 2 }) == Ok 2
attemptReport
attemptReport : (() ->{Fail a, e} b) ->{e} Result (Report a) bSource
Like attempt, but an Err also reports the secondary failures that
cleanup raised while the computation unwound.
import Fail exposing (Fail, fail)
import Result
checked : Int ->{Fail String} Int
checked n = if n > 0 then n else fail "not positive"
Result.mapError { report -> report.primary } (Fail.attemptReport { checked 0 }) == Err "not positive"
fromResult
fromResult : Result a b ->{Fail a} bSource
The value of an Ok, failing with the error of an Err. It joins a
Result-returning call to a failing computation.
import Fail
import Result exposing (Result(..))
parsed : Result String Int
parsed = Ok 1
rejected : Result String Int
rejected = Err "bad"
Fail.attempt { Fail.fromResult parsed + 1 } == Ok 2
Fail.attempt { Fail.fromResult rejected + 1 } == Err "bad"
