Skip to content

Instantly share code, notes, and snippets.

@saiashirwad
Last active December 30, 2024 17:24
Show Gist options
  • Save saiashirwad/814dc5d0e56911a9bae5f9d4e7540232 to your computer and use it in GitHub Desktop.
Save saiashirwad/814dc5d0e56911a9bae5f9d4e7540232 to your computer and use it in GitHub Desktop.
A minimal Either in Typescript, with generator syntax
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")))
@drmgc
Copy link

drmgc commented Dec 30, 2024

It only works when Right is the same type throughout the function, right?

@drmgc
Copy link

drmgc commented Dec 30, 2024

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_}`;
  })

@saiashirwad
Copy link
Author

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_}`;
  })

Good call! I just fixed it!

image

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment