Skip to content

Commit

Permalink
The Fallible<T> redemption arc (#114).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Oct 17, 2024
1 parent b4f77bb commit 71cb7d4
Showing 1 changed file with 54 additions and 2 deletions.
56 changes: 54 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

#### Holistic low-level abstractions

> Todo...
> TODO...
<a name="introduction-how-can-i-support"/>

Expand Down Expand Up @@ -105,4 +105,56 @@ The above Xcode scheme enables optimizations and disables instrumentation that m

#### The `Fallible<T>` redemption arc

> TODO...
The value-error pair is undoubtedly a top-tier recovery mechanism when the value preserves relevant information, like in many binary integer operations. `Fallible<T>` supercharges this idea by adding powerful transformations and ergonomic utilities. It promotes soundness by easing the process of propagating errors. Let's get you up to speed by examining your scalable error-handling arsenal.

##### Propagation: `veto(_:)`

A single error is usually enough to invalidate an operation, but you may want to group all errors corresponding to a semantically meaningful unit of work. The `veto(_:)` operation lets you efficiently combine multiple errors. It forwards its value, along with the logical disjunction of its error indicator and the given argument. Here's how it works in practice.

```swift
U8.max.veto(false).veto(false) // value: 255, error: false
U8.max.veto(false).veto(true ) // value: 255, error: true
U8.max.veto(true ).veto(false) // value: 255, error: true
U8.max.veto(true ).veto(true ) // value: 255, error: true
```

##### Propagation: `map(_:)`

Chaining multiple expressions is a common functional approach and something you may want to consider. The `map(_:)` transforms the underlying value and marges additional error indicators at the end of the given function. Please take a close look at the following example. We'll revisit it in a moment.

```swift
func sumsquare<T: UnsignedInteger>(a: T, b: T) -> Fallible<T> {
a.plus(b).map{$0.squared()}
}
```

##### Propagation: `sink(_:)`

Now that you know the basics of error propagation, let's equip you with the means to take on the world. While `map(_:)` is fantastic, at times, you may have noticed that it sometimes devolves into a pyramid-of-doom. In short, it doesn't scale. But do not fret! You may use the `sink(_:)` method to offload error indicators between operations. Let's rewrite our example by using another formula.

```swift
// tip: Fallible.sink(_:) creates the Bool and calls veto(_:)

func sumsquare<T: UnsignedInteger>(a: T, b: T) -> Fallible<T> {
var w: Bool = false
let x: T = a.squared().sink(&w)
let y: T = a.times(b ).sink(&w).times(2).sink(&w)
let z: T = b.squared().sink(&w)
return x.plus(y).sink(&w).plus(z).veto(w)
}
```

##### Propagation: `optional(_:)`, `result(_:)` and `prune(_:)`

The value-error pair has important properties for writing expressive library code. But what if you're the end user? Good news! Let's take a look at `optional(_:)`, `result(_:)` and `prune(_:)`. The first two methods transform errors into well-understood types with similar names, nice and simple. The last one throws its argument on error. Imagine a laid-back version of `sink(_:)`. Let's rewrite our example again, using `prune(_:)` this time.

```swift
enum Oops: Error { case such, error, much, wow, very, impressive }

func sumsquare<T: UnsignedInteger>(a: T, b: T) throws -> T {
let x: T = try a.squared().prune(Oops.such)
let y: T = try a.times(b ).prune(Oops.error).times(2).prune(Oops.much)
let z: T = try b.squared().prune(Oops.wow)
return try x.plus(y).prune((Oops.very)).plus(z).prune(Oops.impressive)
}
```

0 comments on commit 71cb7d4

Please sign in to comment.