Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(either): add future interoperability #56

Merged
merged 1 commit into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/either/either.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Monad } from '../monad';
import { Matchable } from '../match';
import { Future } from '../future';
import { Futurizable } from '../future/futurizable';

/**
* Abstract class representing a value that can be one of two possible types.
* @template L The type of the left value.
* @template R The type of the right value.
*/
abstract class Either<L, R> implements Monad<R>, Matchable<R, L> {
abstract class Either<L, R> implements Monad<R>, Matchable<R, L>, Futurizable<L | R> {
/**
* Creates a `Right` instance.
* @template T The type of the right value.
Expand Down Expand Up @@ -141,6 +143,19 @@ abstract class Either<L, R> implements Monad<R>, Matchable<R, L> {
* console.log(result.isRight()); // true
*/
abstract isRight(): this is Right<L, R>;

/**
* Converts this `Either` instance into a `Future` instance.
* @returns {Future<L | R>} A `Future` instance containing the value.
* @example
* const result = Either.right(5);
* const asyncClosure: async (x: number) => x * 2
* result.toFuture().map(asyncClosure).complete(
async (value) => expect(await value).toEqual(expected), // 10
async (error) => expect(error).toBeUndefined()
);
*/
abstract toFuture(): Future<L | R>;
}

/**
Expand Down Expand Up @@ -184,6 +199,10 @@ class Left<L, R> extends Either<L, R> {
isRight(): this is Right<L, never> {
return false;
}

toFuture(): Future<L> {
return Future.of(() => Promise.resolve(this.value));
}
}

/**
Expand Down Expand Up @@ -227,6 +246,10 @@ class Right<L, R> extends Either<L, R> {
isRight(): this is Right<never, R> {
return true;
}

toFuture(): Future<R> {
return Future.of(() => Promise.resolve(this.value));
}
}

export { Either, Right, Left };
47 changes: 47 additions & 0 deletions src/future/futurizable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, it, expect } from 'vitest';
import { Either } from '../either';
import { Future } from './future';

const testCasesFutureMapOperation = [
{
type: 'Either Right',
monad: Either.right(2),
assertion: (futureMonad: Future<number> | Future<string>, expected: Future<number> | Future<string>) => {
futureMonad.complete(
(currentValue: number | string) => {
expected.complete(
(expectedValue: number | string) => expect(currentValue).toBe(expectedValue),
(error) => expect(error).toBeUndefined()
);
},
(error) => expect(error).toBeUndefined()
);
},
expected: Future.of(() => Promise.resolve(2)),
},
{
type: 'Either Left',
monad: Either.left('Error'),
assertion: (futureMonad: Future<number> | Future<string>, expected: Future<number> | Future<string>) => {
futureMonad.complete(
(currentValue: number | string) => {
expected.complete(
(expectedValue: number | string) => expect(currentValue).toBe(expectedValue),
(error) => expect(error).toBeUndefined()
);
},
(error) => expect(error).toBeUndefined()
);
},
expected: Future.of(() => Promise.resolve('Error')),
},
];

describe('Futurizable', () => {
it.each(testCasesFutureMapOperation)(
'$type should handle toFuture correctly',
async ({ monad, expected, assertion }) => {
assertion(monad.toFuture(), expected);
}
);
});
5 changes: 5 additions & 0 deletions src/future/futurizable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Future } from './future';

export interface Futurizable<T> {
toFuture(): Future<T>;
}