Skip to content

Instantly share code, notes, and snippets.

@19h

19h/result.ts Secret

Created March 13, 2022 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 19h/56d468bb0ab060c6a7e5202bc44ea78a to your computer and use it in GitHub Desktop.
Save 19h/56d468bb0ab060c6a7e5202bc44ea78a to your computer and use it in GitHub Desktop.
Result impl
/*
homegrown reimplementation of rust Result, a monadic
structure to simplify error handling in a sane way.
*/
import { Either } from 'fp-ts/Either';
import { Validation } from 'io-ts';
import prettyReporter from 'io-ts-reporters';
export type Result<T, E> = ResultOk<T, E> | ResultErr<T, E>;
export class ResultOk<T, E> {
readonly _tag: 'Ok' = 'Ok';
readonly _T!: T;
readonly _E!: E;
constructor(readonly value: T) {
}
is_ok(): this is ResultOk<T, E> {
return true;
}
is_err(): this is never {
return false;
}
map<U>(fn: (arg: T) => U): Result<U, E> {
return new ResultOk<U, E>(fn(this.value));
}
map_or_else<U>(
fn_ok: (arg: T) => U,
fn_err: (arg: E) => U,
): U {
return fn_ok(this.value);
}
map_err<U>(fn: (arg: E) => U): Result<T, U> {
return this as any;
}
and<U>(res: Result<U, E>): Result<U, E> {
return res;
}
and_then<U>(fn: (arg: T) => Result<U, E>): Result<U, E> {
return fn(this.value);
}
or<U>(res: Result<U, E>): Result<U, E> {
return this as any;
}
or_else<U>(fn: (arg: E) => Result<T, U>): Result<T, U> {
return this as any;
}
unwrap_or(optb: T): T {
return this.value;
}
unwrap_or_else(fn: (arg: E) => T): T {
return this.value;
}
unwrap(): T {
return this.value;
}
}
export class ResultErr<T, E> {
readonly _tag: 'Err' = 'Err';
readonly _T!: T;
readonly _E!: E;
constructor(readonly value: E) {
}
is_ok(): this is never {
return false;
}
is_err(): this is ResultErr<T, E> {
return true;
}
map<U>(fn: (arg: T) => U): Result<U, E> {
return this as any;
}
chain<U>(fn: (arg: T) => Result<U, E>): Result<U, E> {
return this as any;
}
map_or_else<U>(
fn_ok: (arg: T) => U,
fn_err: (arg: E) => U,
): U {
return fn_err(this.value);
}
map_err<U>(fn: (arg: E) => U): Result<T, U> {
return new ResultErr<T, U>(fn(this.value));
}
and<U>(res: Result<U, E>): Result<U, E> {
return this as any;
}
and_then<U>(fn: (arg: T) => Result<U, E>): Result<U, E> {
return this as any;
}
or<U>(res: Result<U, E>): Result<U, E> {
return res;
}
or_else<U>(fn: (arg: E) => Result<T, U>): Result<T, U> {
return fn(this.value);
}
unwrap_or(optb: T): T {
return optb;
}
unwrap_or_else(fn: (arg: E) => T): T {
return fn(this.value);
}
unwrap(): never {
throw new Error('Called Result.unwrap() on an Err value: ' + this.value);
}
}
export const Ok = <T, >(val: T) => {
return new ResultOk<T, never>(val);
};
export const Err = <E, >(val: E) => {
return new ResultErr<never, E>(val);
};
export const try_catch = <T>(fn: () => T): Result<T, Error> => {
try {
return Ok(fn());
// @ts-ignore (error is nominally of type any / unknown, not Error)
} catch (error: Error) {
return Err(error);
}
};
export const result_from_either = <T, E>(val: Either<E, T>): Result<T, E> => {
if (val._tag === 'Right') {
return new ResultOk<T, never>(val.right);
}
return new ResultErr<never, E>(val.left);
};
export const result_from_validation = <T>(validation: Validation<T>) => {
if (validation._tag === 'Right') {
return new ResultOk<T, never>(validation.right);
}
const report = prettyReporter.report(validation)
.join(',');
return new ResultErr<never, Error>(new Error(report));
};
export const result_from_promise = async <T>(val: Promise<T>): Promise<Result<T, Error>> => {
try {
return new ResultOk<T, never>(await val);
// @ts-ignore (error is nominally of type any / unknown, not Error)
} catch (error: Error) {
return new ResultErr<never, Error>(error);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment