Last active
April 23, 2021 17:11
-
-
Save timruffles/4aa264059265b412a9bebd007dfaa0a0 to your computer and use it in GitHub Desktop.
operation - like Result and various other ADTs in Haskell etc
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
export type Operation<T, E> = | |
typeof NotStarted | | |
typeof Waiting | | |
Complete<T> | | |
Failed<E> | |
export enum OperationState { | |
NotStartedState = 'NotStarted', | |
WaitingState = 'Waiting', | |
CompleteState = 'Complete', | |
FailedState = 'Failed', | |
} | |
export const NotStarted: { | |
readonly opState: OperationState.NotStartedState | |
} = { | |
opState: OperationState.NotStartedState | |
} | |
export const Waiting: { | |
readonly opState: OperationState.WaitingState | |
} = { | |
opState: OperationState.WaitingState | |
} | |
export class Complete<T> { | |
readonly opState = OperationState.CompleteState | |
constructor( | |
readonly value: T | |
) { | |
} | |
} | |
export class Failed<E> { | |
readonly opState = OperationState.FailedState | |
constructor( | |
readonly error: E | |
) { | |
} | |
static fromCatch(e: Error | any): Failed<{ name: string, message: string }> { | |
if (e instanceof Error) { | |
const {name, message} = e | |
return new Failed({name, message}) | |
} | |
return new Failed({ | |
name: 'Unknown', | |
message: "An unknown error occurred" | |
}) | |
} | |
static fromError(e: Error): Failed<{ name: string, message: string }> { | |
const {name, message} = e | |
return new Failed({name, message}) | |
} | |
} | |
export type Handler<T, E, R> = { | |
notStarted: () => R, | |
waiting: () => R, | |
complete: (t: T) => R, | |
failed: (e: E) => R, | |
} | |
type BooleanHandler<T, R> = { | |
present: (t: T) => R, | |
absent: () => R, | |
} | |
export function isWaiting<T, E>(op: Operation<T, E>): op is typeof Waiting { | |
return op.opState === OperationState.WaitingState | |
} | |
export function isComplete<T, E>(op: Operation<T, E>): op is Complete<T> { | |
return op.opState === OperationState.CompleteState | |
} | |
export function isFailed<E>(op: Operation<any, E>): op is Failed<E> { | |
return op.opState === OperationState.FailedState | |
} | |
export function handle<T, E, R>(op: Operation<T, E>, handler: Handler<T, E, R>): R { | |
switch (op.opState) { | |
case OperationState.CompleteState: | |
return handler.complete(op.value) | |
case OperationState.FailedState: | |
return handler.failed(op.error) | |
case OperationState.NotStartedState: | |
return handler.notStarted() | |
case OperationState.WaitingState: | |
return handler.waiting() | |
} | |
} | |
// transform both value containing cases of the operation - useful for turning a complex op type into a displayable one | |
export const mapOperation = <T, U, E, F>(op: Operation<T, E>, mapComplete: (t:T) => U, mapFailed: (e:E) => F): Operation<U,F> => ( | |
op.opState === OperationState.CompleteState | |
? new Complete(mapComplete(op.value)) | |
: op.opState === OperationState.FailedState | |
? new Failed(mapFailed(op.error)) | |
: op | |
) | |
export const handlePresence = <T, E, R>(op: Operation<T, E>, handler: BooleanHandler<T, R>): R => ( | |
op.opState === OperationState.CompleteState | |
? handler.present(op.value) | |
: handler.absent() | |
) | |
export const mapComplete = <T, E, U>(op: Operation<T, E>, fn: (t: T) => U): Operation<U, E> => ( | |
op.opState === OperationState.CompleteState | |
? new Complete(fn(op.value)) | |
: op | |
) | |
export const mapCompleteAndGet = <T, E, U>(op: Operation<T, E>, fn: (t: T) => U, getAbsent: () => U): U => ( | |
op.opState === OperationState.CompleteState | |
? fn(op.value) | |
: getAbsent() | |
) | |
export const mapAndGetPresence = <T, E, R>(op: Operation<T, E>, handler: BooleanHandler<T, R>): R => ( | |
op.opState === OperationState.CompleteState | |
? handler.present(op.value) | |
: handler.absent() | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment