Skip to content

Commit

Permalink
"Failure" might be complete #bruce #time 15m
Browse files Browse the repository at this point in the history
  • Loading branch information
Bruce Eckel committed Jul 22, 2024
1 parent 445dca9 commit 5a01847
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 30 deletions.
38 changes: 8 additions & 30 deletions Chapters/06_Failure.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,33 +503,10 @@ def run =

We don't see the `ClimateFailure` error, we only get its `message` as produced by the `catchAll`.

## Short-circuiting Failures

Despite the downsides of throwing `Exception`s, there is a reason it is a common practice.
It is a quick and easy way to stop the function when something goes wrong, without wrapping your logic in `if/else`.

With Effects, we achieve the same behavior without the downsides.

This triggers a `gpsFailure`, causing the *first* Effect to fail:

```scala 3 mdoc:runzio
import zio.*

override val bootstrap = gpsFailure

def run =
weatherReport
```

The program fails with the GPS failure, and the `check` Effect does not run.

Short-circuiting is an essential part of user-friendly Effect Systems.
With it, we can write a linear sequence of fallible expressions, while tracking all possible failures.

## Handling Thrown Exceptions

So far our example Effects have **returned** `Exception`s to indicate failure, but you may have legacy code or external libraries which **throw** `Exception`s instead.
In these situations there are ways to wrap the `Exception`-throwing code so that we achieve our preferred style of returning `Exception`s.
So far our example Effects have _returned_ `Exception`s to indicate failure, but you may have legacy code or external libraries that _throw_ `Exception`s instead.
In these situations you can wrap the `Exception`-throwing code to achieve our preferred style of returning `Exception`s.

```scala 3 mdoc:invisible
import zio.*
Expand All @@ -547,7 +524,7 @@ def getTemperatureOrThrow: String =
```

The `getTemperatureOrThrow` function can fail by throwing an `Exception`.
If we call this function from an Effect, a failure causes the program to fail:
If we call this function from an Effect, the program fails:

```scala 3 mdoc:runzio
import zio.*
Expand All @@ -561,7 +538,7 @@ def run =
Despite our claim that this Effect `succeed`s, it crashes with a defect.
When we call side-effecting code, the Effect System can't warn us about the potential failure.

The solution is to use `ZIO.attempt`, which captures thrown `Exception`s as the error type of the Effect:
The solution is to use `ZIO.attempt`, which turns thrown `Exception`s into Effects:

```scala 3 mdoc:silent
import zio.*
Expand All @@ -571,7 +548,8 @@ val safeTemperatureApp =
getTemperatureOrThrow
```

Now you can use any of the failure handling mechanisms in Effects to deal with the failure:
Now you can use an Effect failure-handling mechanism.
Here we use `orElse`:

```scala 3 mdoc:runzio
import zio.*
Expand All @@ -584,7 +562,7 @@ def run =
"Could not get temperature"
```

Here we handle the Effect's failure with a fallback Effect, which succeeds.
The Effect's failure is handled with a fallback, which succeeds.

Thrown `Exception`s are inherently unpredictable, so it is preferable to encapsulate all exception-throwing functions into Effects.
This makes that unpredictability clear and provides mechanisms for handling possible failures.
This makes the unpredictability clear and provides better mechanisms for handling failures.
22 changes: 22 additions & 0 deletions graveyard/06_Short_Circuiting_Failures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Short-circuiting Failures

Despite the downsides of throwing `Exception`s, there is a reason it is a common practice.
It is a quick and easy way to stop the function when something goes wrong, without wrapping your logic in `if/else`.

With Effects, we achieve the same behavior without the downsides.

This triggers a `gpsFailure`, causing the *first* Effect to fail:

```scala 3 mdoc:runzio
import zio.*

override val bootstrap = gpsFailure

def run =
weatherReport
```

The program fails with the GPS failure, and the `check` Effect does not run.

Short-circuiting is an essential part of user-friendly Effect Systems.
With it, we can write a linear sequence of fallible expressions, while tracking all possible failures.

0 comments on commit 5a01847

Please sign in to comment.