1# Cause: Flattened Structure
2
3In v3, `Cause<E>` was a recursive tree with six variants:
4
5```
6Empty | Fail<E> | Die | Interrupt | Sequential<E> | Parallel<E>
7```
8
9The `Sequential` and `Parallel` variants composed causes into a tree to
10represent errors from finalizers or concurrent operations.
11
12In v4, `Cause<E>` has been flattened to a simple wrapper around an array of
13`Reason` values:
14
15```ts
16interface Cause<E> {
17 readonly reasons: ReadonlyArray<Reason<E>>
18}
19
20type Reason<E> = Fail<E> | Die | Interrupt
21```
22
23There are only three reason variants — `Fail`, `Die`, and `Interrupt`. The
24`Empty`, `Sequential`, and `Parallel` variants have been removed. An empty
25cause is represented by an empty `reasons` array. Multiple failures (from
26concurrent or sequential composition) are collected into a flat array.
27
28## Accessing Reasons
29
30**v3** — pattern match on the recursive tree structure:
31
32```ts
33import { Cause } from "effect"
34
35const handle = (cause: Cause.Cause<string>) => {
36 switch (cause._tag) {
37 case "Fail":
38 return cause.error
39 case "Die":
40 return cause.defect
41 case "Empty":
42 return undefined
43 case "Sequential":
44 return handle(cause.left)
45 case "Parallel":
46 return handle(cause.left)
47 case "Interrupt":
48 return cause.fiberId
49 }
50}
51```
52
53**v4** — iterate over the flat `reasons` array:
54
55```ts
56import { Cause } from "effect"
57
58const handle = (cause: Cause.Cause<string>) => {
59 for (const reason of cause.reasons) {
60 switch (reason._tag) {
61 case "Fail":
62 return reason.error
63 case "Die":
64 return reason.defect
65 case "Interrupt":
66 return reason.fiberId
67 }
68 }
69}
70```
71
72## Reason Guards
73
74The v3 type-level guards (`isFailType`, `isDieType`, `isInterruptType`, etc.)
75have been replaced by reason-level guards:
76
77| v3 | v4 |
78| ------------------------------- | --------------------------------- |
79| `Cause.isEmptyType(cause)` | `cause.reasons.length === 0` |
80| `Cause.isFailType(cause)` | `Cause.isFailReason(reason)` |
81| `Cause.isDieType(cause)` | `Cause.isDieReason(reason)` |
82| `Cause.isInterruptType(cause)` | `Cause.isInterruptReason(reason)` |
83| `Cause.isSequentialType(cause)` | Removed |
84| `Cause.isParallelType(cause)` | Removed |
85
86## Cause-Level Predicates
87
88| v3 | v4 |
89| -------------------------------- | -------------------------------- |
90| `Cause.isFailure(cause)` | `Cause.hasFails(cause)` |
91| `Cause.isDie(cause)` | `Cause.hasDies(cause)` |
92| `Cause.isInterrupted(cause)` | `Cause.hasInterrupts(cause)` |
93| `Cause.isInterruptedOnly(cause)` | `Cause.hasInterruptsOnly(cause)` |
94
95## Constructors
96
97| v3 | v4 |
98| ------------------------------- | ---------------------------- |
99| `Cause.empty` | `Cause.empty` |
100| `Cause.fail(error)` | `Cause.fail(error)` |
101| `Cause.die(defect)` | `Cause.die(defect)` |
102| `Cause.interrupt(fiberId)` | `Cause.interrupt(fiberId)` |
103| `Cause.sequential(left, right)` | `Cause.combine(left, right)` |
104| `Cause.parallel(left, right)` | `Cause.combine(left, right)` |
105
106In v4, `Cause.combine` concatenates the `reasons` arrays of two causes. The
107distinction between sequential and parallel composition is no longer
108represented in the data structure.
109
110## Extractors
111
112| v3 | v4 |
113| ------------------------------ | ------------------------------------------ |
114| `Cause.failureOption(cause)` | `Cause.findErrorOption(cause)` |
115| `Cause.failureOrCause(cause)` | `Cause.findError(cause)` |
116| `Cause.dieOption(cause)` | `Cause.findDefect(cause)` |
117| `Cause.interruptOption(cause)` | `Cause.findInterrupt(cause)` |
118| `Cause.failures(cause)` | `cause.reasons.filter(Cause.isFailReason)` |
119| `Cause.defects(cause)` | `cause.reasons.filter(Cause.isDieReason)` |
120| `Cause.interruptors(cause)` | `Cause.interruptors(cause)` |
121
122Note: `findError` and `findDefect` return `Result.Result` instead of `Option`.
123Use `findErrorOption` for the `Option`-based variant.
124
125## Error Classes
126
127All `*Exception` classes have been renamed to `*Error`:
128
129| v3 | v4 |
130| -------------------------------------- | ----------------------------- |
131| `Cause.NoSuchElementException` | `Cause.NoSuchElementError` |
132| `Cause.TimeoutException` | `Cause.TimeoutError` |
133| `Cause.IllegalArgumentException` | `Cause.IllegalArgumentError` |
134| `Cause.ExceededCapacityException` | `Cause.ExceededCapacityError` |
135| `Cause.UnknownException` | `Cause.UnknownError` |
136| `Cause.RuntimeException` | Removed |
137| `Cause.InterruptedException` | Removed |
138| `Cause.InvalidPubSubCapacityException` | Removed |
139
140The corresponding guards follow the same pattern:
141
142| v3 | v4 |
143| -------------------------------------- | ---------------------------------- |
144| `Cause.isNoSuchElementException(u)` | `Cause.isNoSuchElementError(u)` |
145| `Cause.isTimeoutException(u)` | `Cause.isTimeoutError(u)` |
146| `Cause.isIllegalArgumentException(u)` | `Cause.isIllegalArgumentError(u)` |
147| `Cause.isExceededCapacityException(u)` | `Cause.isExceededCapacityError(u)` |
148| `Cause.isUnknownException(u)` | `Cause.isUnknownError(u)` |
149
150## New in v4
151
152- **`Cause.fromReasons(reasons)`** — construct a `Cause` from an array of
153 `Reason` values.
154- **`Cause.makeFailReason(error)`**, **`Cause.makeDieReason(defect)`**,
155 **`Cause.makeInterruptReason(fiberId)`** — construct individual `Reason`
156 values.
157- **`Cause.annotate(cause, annotations)`** — attach annotations to a `Cause`.
158- **`Cause.findFail(cause)`**, **`Cause.findDie(cause)`**,
159 **`Cause.findInterrupt(cause)`** — extract specific reason types using
160 the `Result` module.
161- **`Cause.filterInterruptors(cause)`** — extract interrupting fiber IDs as
162 a `Result`.
163- **`Cause.Done`** — a graceful completion signal for queues and streams.