Skip to content

Instantly share code, notes, and snippets.

@tchak
Created July 29, 2021 06:26
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 tchak/d151afe36aeec0a87b71b5ea9ee933ed to your computer and use it in GitHub Desktop.
Save tchak/d151afe36aeec0a87b71b5ea9ee933ed to your computer and use it in GitHub Desktop.
import type { Maybe } from 'true-myth/maybe';
import { Result, ok, err, isInstance as isResult } from 'true-myth/result';
export interface Task<Value, Error> {
(): Promise<Result<Value, Error>>;
map<MappedValue>(f: (t: Value) => MappedValue): Task<MappedValue, Error>;
mapErr<MappedError>(f: (e: Error) => MappedError): Task<Value, MappedError>;
flatMap<MappedValue, MappedError>(
f: (t: Value) => Task<MappedValue, MappedError>
): Task<MappedValue, Error | MappedError>;
mapOr<MappedValue>(
or: MappedValue,
f: (t: Value) => MappedValue
): Promise<MappedValue>;
mapOrElse<MappedValue>(
orElse: (e: Error) => MappedValue,
f: (t: Value) => MappedValue
): Promise<MappedValue>;
tap(f: (t: Value) => void): Task<Value, Error>;
tapErr(f: (e: Error) => void): Task<Value, Error>;
}
export function fromThunk<Value, Error>(
thunk: () => Promise<Value> | Promise<Result<Value, Error>>
): Task<Value, Error> {
const task = () =>
thunk().then(
(value) =>
isResult<Value, Error>(value) ? value : ok<Value, Error>(value),
(e: Error) => err<Value, Error>(e)
);
task.map = <MappedValue>(
f: (t: Value) => MappedValue
): Task<MappedValue, Error> => map(f, task);
task.mapErr = <MappedError>(
f: (t: Error) => MappedError
): Task<Value, MappedError> => mapErr(f, task);
task.flatMap = <MappedValue, MappedError>(
f: (t: Value) => Task<MappedValue, MappedError>
): Task<MappedValue, Error | MappedError> => flatMap(f, task);
task.mapOr = <MappedValue>(
or: MappedValue,
f: (t: Value) => MappedValue
): Promise<MappedValue> => mapOr(or, f, task);
task.mapOrElse = <MappedValue>(
orElse: (e: Error) => MappedValue,
f: (t: Value) => MappedValue
): Promise<MappedValue> => mapOrElse(orElse, f, task);
task.tap = (f: (t: Value) => void): Task<Value, Error> => tap(f, task);
task.tapErr = (f: (e: Error) => void): Task<Value, Error> => tapErr(f, task);
return task;
}
export function fromPromise<Value, Error>(
promise: Promise<Value>
): Task<Value, Error> {
return fromThunk(() => promise);
}
export function fromResult<Value, Error>(
result: Result<Value, Error>
): Task<Value, Error> {
return fromThunk<Value, Error>(async () => result);
}
export function fromMaybe<Value, Error>(
error: Error,
maybe: Maybe<Value>
): Task<Value, Error> {
return fromResult(maybe.toOkOrErr(error));
}
export function map<Value, MappedValue, Error>(
f: (t: Value) => MappedValue,
task: Task<Value, Error>
): Task<MappedValue, Error> {
return fromThunk<MappedValue, Error>(() =>
task().then<Result<MappedValue, Error>>((t) => t.map<MappedValue>(f))
);
}
export function mapErr<Error, MappedError, Value>(
f: (t: Error) => MappedError,
task: Task<Value, Error>
): Task<Value, MappedError> {
return fromThunk<Value, MappedError>(() =>
task().then<Result<Value, MappedError>>((t) => t.mapErr<MappedError>(f))
);
}
export function tap<Value, Error>(
f: (t: Value) => void,
task: Task<Value, Error>
): Task<Value, Error> {
return fromThunk<Value, Error>(() =>
task().then<Result<Value, Error>>((t) => {
if (t.isOk()) {
f(t.value);
}
return t;
})
);
}
export function tapErr<Error, Value>(
f: (t: Error) => void,
task: Task<Value, Error>
): Task<Value, Error> {
return fromThunk<Value, Error>(() =>
task().then<Result<Value, Error>>((t) => {
if (t.isErr()) {
f(t.error);
}
return t;
})
);
}
export function mapOr<Value, MappedValue, Error>(
or: MappedValue,
f: (t: Value) => MappedValue,
task: Task<Value, Error>
): Promise<MappedValue> {
return task().then((t) => t.mapOr(or, f));
}
export function mapOrElse<Value, MappedValue, Error>(
orElse: (e: Error) => MappedValue,
f: (t: Value) => MappedValue,
task: Task<Value, Error>
): Promise<MappedValue> {
return task().then((t) => t.mapOrElse(orElse, f));
}
export function getOrElse<Value, MappedValue, Error>(
orElse: (e: Error) => MappedValue,
task: Task<Value, Error>
): Promise<Value | MappedValue> {
return task().then((t) => t.unwrapOrElse(orElse));
}
export function getOr<Value, MappedValue, Error>(
or: MappedValue,
task: Task<Value, Error>
): Promise<Value | MappedValue> {
return task().then((t) => t.unwrapOr(or));
}
export function flatMap<Value, Error, MappedValue, MappedError>(
f: (t: Value) => Task<MappedValue, MappedError>,
task: Task<Value, Error>
): Task<MappedValue, Error | MappedError> {
return fromThunk<MappedValue, Error | MappedError>(() =>
task().then<Result<MappedValue, Error | MappedError>>((t) => {
if (t.isOk()) {
return f(t.value)();
} else {
return err<MappedValue, Error | MappedError>(t.error);
}
})
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment