Skip to content

Commit 0b72951

Browse files
committed
Cleanup README.md, with some help from CoPilot
1 parent 2d9e46a commit 0b72951

File tree

1 file changed

+45
-38
lines changed

1 file changed

+45
-38
lines changed

README.md

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
# maybe-result - Safe function return handling in Typescript and Javascript
22

3-
Deciding when a function should return an undefined, throw an `Error`, or returning some other sort of
4-
"something isn't right" is tough. That's because we don't always know how users _calling_ our function
5-
will work, and because we want to be clear, clean, and safe.
3+
Deciding when a function should return `undefined`, throw an `Error`, or return some other indicator that
4+
"something isn't right" is tough. We don't always know how users _calling_ our function
5+
will use it, and we want to be clear, clean, and safe.
66

77
This library provides two approaches for wrapping function results:
88

99
- [Maybe](#maybe) for when something may or may not exist
10-
- [Result](#result) for when you want to return an error, but let the caller decide how to handle it
10+
- [Result](#result) for when you might have an error, but want to let the caller decide how to handle it
1111

1212
## Maybe
1313

14-
In many languages, we have concepts of exceptions but also a `null` value of some sort.
14+
In many languages, we have concepts of exceptions and `null` values.
1515
(JavaScript has both `null` and `undefined`. Ugh!)
1616

1717
Often a function will need to indicate when a value _maybe_ exists, or it does not.
1818
In JavaScript, the "does not" is usually returned as `undefined` or `null`, but sometimes
19-
a function will *throw* an `Error` type instead. Thus, the developer needs to figure out
19+
a function will _throw_ an `Error` type instead. Thus, the developer needs to figure out
2020
how that particular function behaves and adapt to that if they want to handle
2121
the missing value.
2222

2323
Finally, throwing Errors in TypeScript can be expensive, as a stack trace must be
2424
generated and cross-referenced to the `.js.map` files. These stack traces to your
25-
TypeScript source are immensely useful to trace actual errors, but are wasted
25+
TypeScript source are immensely useful for tracing actual errors, but they are wasted
2626
processing when ignored.
2727

2828
The `Maybe` type makes this cleaner. Elm was an early language that defined this.
@@ -44,9 +44,9 @@ and immutable alternative to `undefined` and `null`.
4444
Here's a nice introduction to the concept:
4545
[Implementing a Maybe Pattern using a TypeScript Type Guard](https://medium.com/@sitapati/implementing-a-maybe-pattern-using-a-typescript-type-guard-81b55efc0af0)
4646

47-
### Example by story
47+
### Example by Story
4848

49-
You might have defined a data repository class (access to a data store) like this:
49+
You might define a data repository class (access to a data store) like this:
5050

5151
```ts
5252
class WidgetRepository {
@@ -56,21 +56,22 @@ class WidgetRepository {
5656
}
5757
```
5858

59-
If the Widget isn't found, you throw a `NotFoundError`. All is well, until you start _expecting_
59+
If the Widget isn't found, you throw a `NotFoundError`. All is well until you start _expecting_
6060
a Widget not to be found. That becomes valid flow, so you find yourself writing this a lot:
6161

6262
```ts
63-
let widget: Widget | undefined;
64-
try {
65-
widget = await widgetRepo.get(widgetID);
63+
let widget: Widget | undefined;
64+
try {
65+
widget = await widgetRepo.get(widgetID);
66+
} catch (error) {
67+
if (!(error instanceof NotFoundError)) {
68+
throw error;
6669
}
67-
catch (error) {
68-
if (!(error instanceof NotFoundError)) {
69-
throw error;
70-
}
71-
}
72-
73-
if (widget) { /* ... */ }
70+
}
71+
72+
if (widget) {
73+
/* ... */
74+
}
7475
```
7576

7677
You may be willing to do that once... but not more. So you first try to change the repository:
@@ -83,7 +84,7 @@ class WidgetRepository {
8384
}
8485
```
8586

86-
Now it returns `undefined` instead of throwing. Oh, but what a hassle now you have to _check_ for
87+
Now it returns `undefined` instead of throwing. Oh, but what a hassle - now you have to _check_ for
8788
`undefined` _every time_ you call the function! So instead, you define _two_ functions:
8889

8990
```ts
@@ -99,7 +100,7 @@ class WidgetRepository {
99100

100101
That makes it easier. It works. You just have to write _two_ functions every time you write a get function. 🙄
101102

102-
**OR...** use Maybe
103+
**OR...** use `Maybe` 🎉
103104

104105
```ts
105106
class WidgetRepository {
@@ -123,27 +124,33 @@ if (widget) {
123124
const widget = (await widgetRepo.get(widgetID)).unwrapOr(defaultWidget);
124125
```
125126

127+
---
128+
126129
There are many other functions both on the `Maybe` instance and static helper functions in
127130
the `Maybe` namespace.
128131

129132
## Result
130133

131-
Unlike `Maybe`, which simple has some value or no value and don't want to return `undefined`,
134+
Unlike `Maybe`, which simply has some value or no value and doesn't want to return `undefined`,
132135
`Result` is for when you have an **error** and don't want to `throw`.
133-
Similar to `Maybe`, this is really all about the function giving the _caller_ the choice of
136+
Similar to `Maybe`, this is all about the function giving the caller the _choice_ of
134137
how to handle a situation - in this case an _exceptional_ situation.
135138

136-
This is modeled directly off of the Rust `Result` type, but made to pair cleanly with this implementation of `Maybe`.
139+
This is modeled off of the Rust `Result` type, but made to pair cleanly with this
140+
implementation of `Maybe`.
137141

138142
### Example
139143

140-
Expanding on the previous example of a `WidgetRepository`, let's add a function that creates a new widget
141-
in the repository. A `create` function should error out though if the assumption that the widget doesn't
142-
yet exist is false.
144+
Expanding on the previous example of a `WidgetRepository`,
145+
let's add a function in the repository that creates a new widget.
146+
A `create` function should error out if the assumption that the
147+
widget doesn't yet exist is false.
143148

144149
```ts
145150
class WidgetRepository {
146-
async create(widget: CreatableWidget): Promise<Result<Widget,ConstraintError>> {
151+
async create(
152+
widget: CreatableWidget,
153+
): Promise<Result<Widget, ConstraintError>> {
147154
try {
148155
// implementation ...
149156
return Result.okay(newWidget);
@@ -189,23 +196,23 @@ Both `Maybe` and `Result` have many more member and static functions. Learn more
189196
- [API Documentation](https://www.jsdocs.io/package/maybe-result)
190197
- Full coverage examples in the [Maybe unit test suite](src/maybe.spec.ts) and [Result unit test suite](src/result.spec.ts).
191198
- Functions are named per some foundational concepts:
192-
- `wrap` wraps up a value in a `Maybe` or `Result`
193-
- `unwrap` means to extract the value contained in a `Maybe` or `Result`
194-
- `or` performs a boolean _or_ operation between two `Maybe` or `Result` instances
195-
- `orElse` lazily gets the second operand for an _or_ operation via a callback function _only_ if needed
196-
- `and` performs a boolean _and_ operation between two `Maybe` or `Result` instances
199+
- `wrap` wraps up a value
200+
- `unwrap` means to extract the value
201+
- `or` performs a boolean _or_ operation between two instances
202+
- `orElse` lazily gets the second operand for an _or_ operation via a callback function _only_ if needed
203+
- `and` performs a boolean _and_ operation between two instances
197204
- `andThen` lazily gets the second operand for an _and_ operation via a callback function _only_ if needed
198-
- `map` functions transform the value in a `Maybe` or `Result` to return a new instance (immutably)
205+
- `map` functions transform the value to return a new instance (immutably)
199206

200207
## Origin and Alternatives
201208

202209
This implementation is based on [ts-results](https://github.com/vultix/ts-results),
203-
which adheres to the Rust API.
210+
which adheres to the Rust API.
204211
This library has more natual word choices, Promise support, additional functions, and other enhancements.
205212

206213
There are many other libraries that do this same thing - just
207214
[search NPM for "maybe"](https://www.npmjs.com/search?q=maybe).
208215
It is up to you to decide which option is best for your project.
209216

210-
**The goal of this library is to be featureful, safe, and easy to understand without
211-
a study of functional programming.**
217+
_The goal of this library is to be featureful, safe, and easy to understand without
218+
a study of functional programming._

0 commit comments

Comments
 (0)