Created
July 29, 2021 06:26
-
-
Save tchak/d151afe36aeec0a87b71b5ea9ee933ed to your computer and use it in GitHub Desktop.
This file contains 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
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