Skip to content

Commit

Permalink
Remove the parameter spread variants of Result.any and all (#191)
Browse files Browse the repository at this point in the history
Since [1] Result.any and Result.all supported two ways of being called:

    # 1. Parameter spread
    Result.any(result1, result2)

    # 2. An array
    Result.any([result1, result2])

I believe the array variant is much more realistic than the parameter
spread version. Additionally the parameter spread version can lead to
nasty stack overflow surprises[2] so I figured let's remove support for
it.

Bonus: simpler API (and API documentation).

There's no function lost here.

[1] 2e03308 ("Add non-spread overload for Result.all (#125)")
[2] vultix#85
  • Loading branch information
jstasiak authored Feb 6, 2025
1 parent 39fa078 commit ce807ef
Show file tree
Hide file tree
Showing 4 changed files with 3 additions and 89 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Backwards incompatible:
```

Iterating `None` and `Err` is not affected and continues to produce no results.
- Removed the parameter spread variants of `Result.all` and `Result.any`. Both of these
methods now only take a single array parameter (the array parameter has already been
supported for a while).

Fixed:

Expand Down
29 changes: 0 additions & 29 deletions docs/reference/result.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,13 @@ to combine results with asynchronouse code.
.. code-block:: typescript
// The actual signature is more complicated but this should be good enough.
static all(...results: Result<T, E>): Result<T[], E>
static all(results: Result<T, E>[]): Result<T[], E>
Parse a set of ``Result``, returning an array of all ``Ok`` values.
Short circuits with the first ``Err`` found, if any.

Example:

.. code-block:: typescript
let pizzaResult: Result<Pizza, GetPizzaError> = getPizzaSomehow();
let toppingsResult: Result<Toppings, GetToppingsError> = getToppingsSomehow();
let result = Result.all(pizzaResult, toppingsResult); // Result<[Pizza, Toppings], GetPizzaError | GetToppingsError>
let [pizza, toppings] = result.unwrap(); // pizza is a Pizza, toppings is a Toppings. Could throw GetPizzaError or GetToppingsError.
When working with a set of results of unknown size, you can use the array version of ``all()``.

Example:

.. code-block:: typescript
let results: Result<Topping, GetToppingsError>[] = pizzaToppingNames.map(name => getPizzaToppingByName(name));
Expand Down Expand Up @@ -99,28 +85,13 @@ Example:
.. code-block:: typescript
// The actual signature is more complicated but this should be good enough.
static any(...results: Result<T, E>): Result<T, E[]>
static any(results: Result<T, E>[]): Result<T, E[]>
Parse a set of ``Result``, short-circuits when an input value is ``Ok``.
If no ``Ok`` is found, returns an ``Err`` containing the collected error values.

Example:

.. code-block:: typescript
let url1: Result<string, Error1> = attempt1();
let url2: Result<string, Error2> = attempt2();
let url3: Result<string, Error3> = attempt3();
let result = Result.any(url1, url2, url3); // Result<string, Error1 | Error2 | Error3>
let url = result.unwrap(); // At least one attempt gave us a successful url
When working with a set of results of unknown size, you can use the array version of ``any()``.

Example:

.. code-block:: typescript
let connections: Array<Result<string, Error1 | Error2 | Error3 | ...>> = [attempt1(), attempt2(), attempt3(), ...];
Expand Down
22 changes: 0 additions & 22 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,16 +456,7 @@ export namespace Result {
*/
export function all<const T extends Result<any, any>[]>(
results: T,
): Result<ResultOkTypes<T>, ResultErrTypes<T>[number]>;
export function all<T extends Result<any, any>[]>(
...results: T
): Result<ResultOkTypes<T>, ResultErrTypes<T>[number]>;
export function all<T extends Result<any, any>[]>(
arg0: Head<T> | T,
...argN: Tail<T>
): Result<ResultOkTypes<T>, ResultErrTypes<T>[number]> {
const results = arg0 === undefined ? [] : Array.isArray(arg0) ? (arg0 as T) : ([arg0, ...argN] as T);

const okResult = [];
for (let result of results) {
if (result.isOk()) {
Expand All @@ -484,16 +475,7 @@ export namespace Result {
*/
export function any<const T extends Result<any, any>[]>(
results: T,
): Result<ResultOkTypes<T>[number], ResultErrTypes<T>>;
export function any<T extends Result<any, any>[]>(
...results: T
): Result<ResultOkTypes<T>[number], ResultErrTypes<T>>;
export function any<T extends Result<any, any>[]>(
arg0: Head<T> | T,
...argN: Tail<T>
): Result<ResultOkTypes<T>[number], ResultErrTypes<T>> {
const results = arg0 === undefined ? [] : Array.isArray(arg0) ? (arg0 as T) : ([arg0, ...argN] as T);

const errResult = [];

// short-circuits
Expand Down Expand Up @@ -552,7 +534,3 @@ export namespace Result {
return val instanceof Err || val instanceof Ok;
}
}

// Utility types
type Head<T extends any[]> = T extends [any, ...infer R] ? (T extends [...infer F, ...R] ? F : never) : never;
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : never;
38 changes: 0 additions & 38 deletions test/result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,40 +106,21 @@ test('Result.all', () => {
const err1 = new Err(Error());
const err2 = new Err(9 as const) as Result<boolean, 9>;

const all0 = Result.all();
expect(all0).toMatchResult(Ok([]));
eq<typeof all0, Result<[], never>>(true);

const all0_array = Result.all([]);
expect(all0_array).toMatchResult(Ok([]));
eq<typeof all0_array, Result<[], never>>(true);

const all1 = Result.all(ok0, ok1);
expect(all1).toMatchResult(Ok([3, true]));
eq<typeof all1, Result<[number, boolean], never>>(true);

const all1Array = Result.all([ok0, ok1]);
expect(all1Array).toMatchResult(Ok([3, true]));
eq<typeof all1Array, Result<[number, boolean], never>>(true);

const all2 = Result.all(err0, err1);
expect(all2).toMatchResult(Err(err0.error));
eq<typeof all2, Result<[never, never], symbol | Error>>(true);

const all2Array = Result.all([err0, err1]);
expect(all2Array).toMatchResult(Err(err0.error));
eq<typeof all2Array, Result<[never, never], symbol | Error>>(true);

const all3 = Result.all(...([] as Result<string, number>[]));
eq<typeof all3, Result<string[], number>>(true);

const all3Array = Result.all([] as Result<string, number>[]);
eq<typeof all3Array, Result<string[], number>>(true);

const all4 = Result.all(ok0, ok1, ok2, err2);
expect(all4).toMatchResult(Err(9));
eq<typeof all4, Result<[number, boolean, 8, boolean], boolean | 9>>(true);

const all4Array = Result.all([ok0, ok1, ok2, err2]);
expect(all4Array).toMatchResult(Err(9));
eq<typeof all4Array, Result<[number, boolean, 8, boolean], boolean | 9>>(true);
Expand All @@ -153,40 +134,21 @@ test('Result.any', () => {
const err1 = new Err(Error());
const err2 = new Err(9 as const) as Result<boolean, 9>;

const any0 = Result.any();
expect(any0).toMatchResult(Err([]));
eq<typeof any0, Result<never, []>>(true);

const any0Array = Result.any([]);
expect(any0Array).toMatchResult(Err([]));
eq<typeof any0Array, Result<never, []>>(true);

const any1 = Result.any(ok0, ok1);
expect(any1).toMatchResult(Ok(3));
eq<typeof any1, Result<number | boolean, [never, never]>>(true);

const any1Array = Result.any([ok0, ok1]);
expect(any1Array).toMatchResult(Ok(3));
eq<typeof any1Array, Result<number | boolean, [never, never]>>(true);

const any2 = Result.any(err0, err1);
expect(any2).toMatchResult(Err([err0.error, err1.error]));
eq<typeof any2, Result<never, [symbol, Error]>>(true);

const any2Array = Result.any([err0, err1]);
expect(any2Array).toMatchResult(Err([err0.error, err1.error]));
eq<typeof any2Array, Result<never, [symbol, Error]>>(true);

const any3 = Result.any(...([] as Result<string, number>[]));
eq<typeof any3, Result<string, number[]>>(true);

const any3Array = Result.any([] as Result<string, number>[]);
eq<typeof any3Array, Result<string, number[]>>(true);

const any4 = Result.any(err0, err1, err2, ok2);
expect(any4).toMatchResult(Ok(8));
eq<typeof any4, Result<boolean | 8, [symbol, Error, 9, boolean]>>(true);

const any4Array = Result.any([err0, err1, err2, ok2]);
expect(any4Array).toMatchResult(Ok(8));
eq<typeof any4Array, Result<boolean | 8, [symbol, Error, 9, boolean]>>(true);
Expand Down

0 comments on commit ce807ef

Please sign in to comment.