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

primary

primary : errorSource

The typed failure that ended the computation.

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

primary

primary : errorSource

The typed failure that ended the computation.

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.

Operations

fail

abort fail : error ->{Fail error} aSource

Fails with error. It never returns: it unwinds to the nearest enclosing attempt, which answers Err error.

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"