Add AsyncDoEither class
This commit is contained in:
parent
07a74315e1
commit
de0160fd9e
134
src/either.ts
134
src/either.ts
@ -176,3 +176,137 @@ export class DoEither<T extends Record<string, unknown>, E> {
|
||||
return new DoEither(right({}))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for working with Either in a do-notation style with asynchronous computations
|
||||
* @typeParam T - The type of the accumulated scope
|
||||
* @typeParam E - The error type
|
||||
*/
|
||||
export class AsyncDoEither<T extends Record<string, unknown>, E> {
|
||||
/**
|
||||
* @param promise - The current Promise of an Either value
|
||||
*/
|
||||
constructor(private readonly promise: Promise<Either<T, E>>) {}
|
||||
|
||||
/**
|
||||
* Binds a new value to the scope synchronously
|
||||
* @typeParam K - The key to bind to
|
||||
* @typeParam A - The type of the value to bind
|
||||
* @param key - The property name to bind to
|
||||
* @param fa - The Either value to bind
|
||||
* @returns A new AsyncDoEither instance with the extended scope
|
||||
*/
|
||||
bind<K extends string, A>(
|
||||
key: K,
|
||||
fa: Either<A, E>,
|
||||
): AsyncDoEither<T & Record<K, A>, E> {
|
||||
return this.bindL(key, () => fa)
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a new value to the scope asynchronously
|
||||
* @typeParam K - The key to bind to
|
||||
* @typeParam A - The type of the value to bind
|
||||
* @param key - The property name to bind to
|
||||
* @param f - Function returning Either or Promise of Either
|
||||
* @returns A new AsyncDoEither instance with the extended scope
|
||||
*/
|
||||
bindL<K extends string, A>(
|
||||
key: K,
|
||||
f: (scope: T) => Either<A, E> | Promise<Either<A, E>>,
|
||||
): AsyncDoEither<T & Record<K, A>, E> {
|
||||
const newPromise = this.promise.then(async (currentEither) => {
|
||||
if (isLeft(currentEither)) {
|
||||
return currentEither as unknown as Either<T & Record<K, A>, E>
|
||||
}
|
||||
|
||||
const scope = currentEither.right
|
||||
try {
|
||||
const nextEither = await f(scope)
|
||||
|
||||
if (isLeft(nextEither)) {
|
||||
return nextEither as unknown as Either<T & Record<K, A>, E>
|
||||
}
|
||||
|
||||
return right({
|
||||
...scope,
|
||||
[key]: nextEither.right,
|
||||
}) as Either<T & Record<K, A>, E>
|
||||
} catch (err) {
|
||||
return left(err as E) as Either<T & Record<K, A>, E>
|
||||
}
|
||||
})
|
||||
|
||||
return new AsyncDoEither(newPromise)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the final result from the scope
|
||||
* @typeParam B - The return type
|
||||
* @param f - The function to transform the scope
|
||||
* @returns A Promise of Either containing the result
|
||||
*/
|
||||
return<B>(f: (scope: T) => B): Promise<Either<B, E>> {
|
||||
return this.promise.then(either => {
|
||||
if (isLeft(either)) {
|
||||
return either as unknown as Either<B, E>
|
||||
}
|
||||
return right(f(either.right))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current scope
|
||||
* @returns A Promise of the current Either scope
|
||||
*/
|
||||
done(): Promise<Either<T, E>> {
|
||||
return this.promise
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an effect without modifying the scope
|
||||
* @param fa - The Either effect to execute
|
||||
* @returns A new AsyncDoEither instance with the same scope
|
||||
*/
|
||||
do(fa: Either<unknown, E>): AsyncDoEither<T, E> {
|
||||
return this.doL(() => fa)
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an effect that depends on the current scope
|
||||
* @param f - Function returning Either or Promise of Either
|
||||
* @returns A new AsyncDoEither instance with the same scope
|
||||
*/
|
||||
doL(f:
|
||||
(scope: T) =>
|
||||
| Either<unknown, E>
|
||||
| Promise<Either<unknown, E>>): AsyncDoEither<T, E>
|
||||
{
|
||||
const newPromise = this.promise.then(async (currentEither) => {
|
||||
if (isLeft(currentEither)) {
|
||||
return currentEither
|
||||
}
|
||||
|
||||
const scope = currentEither.right
|
||||
try {
|
||||
const effectEither = await f(scope)
|
||||
if (isLeft(effectEither)) {
|
||||
return effectEither as unknown as Either<T, E>
|
||||
}
|
||||
return currentEither
|
||||
} catch (err) {
|
||||
return left(err as E)
|
||||
}
|
||||
})
|
||||
|
||||
return new AsyncDoEither(newPromise)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new AsyncDoEither chain with an empty scope
|
||||
* @returns A new AsyncDoEither instance with an empty scope
|
||||
*/
|
||||
static start<E>(): AsyncDoEither<{}, E> {
|
||||
return new AsyncDoEither(Promise.resolve(right({})))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user