Last active
December 30, 2024 17:24
-
-
Save saiashirwad/814dc5d0e56911a9bae5f9d4e7540232 to your computer and use it in GitHub Desktop.
A minimal Either in Typescript, with generator syntax
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type Either<R, L> = Left<L, R> | Right<R, L> | |
export class Left<L, R = never> { | |
readonly _tag = "Left" | |
constructor(public readonly left: L) {} | |
*[Symbol.iterator](): Generator<Either<R, L>, R, any> { | |
return yield this | |
} | |
} | |
export class Right<R, L> { | |
readonly _tag = "Right" | |
constructor(public readonly right: R) {} | |
*[Symbol.iterator](): Generator<Either<R, L>, R, any> { | |
return yield this | |
} | |
} | |
export const Either = { | |
left<L, R = never>(l: L): Either<R, L> { | |
return new Left(l) | |
}, | |
right<R, L = never>(r: R): Either<R, L> { | |
return new Right(r) | |
}, | |
isLeft<R, L>(either: Either<R, L>): either is Left<L, R> { | |
return either._tag === "Left" | |
}, | |
isRight<R, L>( | |
either: Either<R, L>, | |
): either is Right<R, L> { | |
return either._tag === "Right" | |
}, | |
match<R, L, T>( | |
either: Either<R, L>, | |
patterns: { | |
onLeft: (left: L) => T | |
onRight: (right: R) => T | |
}, | |
): T { | |
if (Either.isLeft(either)) { | |
return patterns.onLeft(either.left) | |
} | |
return patterns.onRight(either.right) | |
}, | |
gen<R, L>( | |
f: () => Generator<Either<any, L>, R, any>, | |
): Either<R, L> { | |
const iterator = f() | |
let current = iterator.next() | |
while (!current.done) { | |
const either = current.value | |
if (Either.isLeft(either)) { | |
return either | |
} | |
current = iterator.next(either.right) | |
} | |
return Either.right(current.value) | |
}, | |
} | |
const fn = ( | |
a: Either<number, string>, | |
b: Either<number, string>, | |
) => | |
Either.gen(function* () { | |
const a_ = yield* a | |
const b_ = yield* b | |
if (a_ > b_) { | |
return yield* Either.left("a is greater than b") | |
} | |
return a_ + b_ | |
}) | |
console.log(fn(Either.right(5), Either.right(3))) | |
console.log(fn(Either.right(5), Either.left("hi"))) |
For example:
const fn = (
a: Either<number, Error>,
b: Either<string, Error>,
) =>
Either.gen(function* () {
const a_ = yield* a;
const b_ = yield* b;
if (a_ > b_) {
return yield* Either.left(new Error("a is greater than b"));
}
return `${a_} ${b_}`;
})
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It only works when
Right
is the same type throughout the function, right?