cli-framework/src/lib/util/basic/option.ts

56 lines
1.6 KiB
TypeScript

export type None = {readonly _tag: 'None'}
export type Some<A> = {readonly _tag: 'Some', readonly value: A}
export type Option<A> = Some<A> | None
// Constructors
export const none: Option<never> = {_tag: 'None'}
export const some = <A>(value: A): Option<A> => ({
_tag: 'Some',
value,
})
// Operations
export const map = <A, B>(f: (a: A) => B) => (fa: Option<A>): Option<B> =>
fa._tag === 'Some' ? some(f(fa.value)) : none
export const chain =
<A, B>(f: (a: A) => Option<B>) => (fa: Option<A>): Option<B> =>
fa._tag === 'Some' ? f(fa.value) : none
export const getOrElse = <A>(defaultValue: A) => (fa: Option<A>): A =>
fa._tag === 'Some' ? fa.value : defaultValue
export class DoOption<T extends Record<string, unknown>> {
constructor(private readonly option: Option<T>) {}
bind<K extends string, A>(key: K, fa: Option<A>): DoOption<T & Record<K, A>> {
const newOption = chain((scope: T) =>
map((a: A) => ({...scope, [key]: a}))(fa)
)(this.option)
return new DoOption(newOption as Option<T & Record<K, A>>)
}
return<B>(f: (scope: T) => B): Option<B> {
return map(f)(this.option)
}
done(): Option<T> {
return this.option
}
do(fa: Option<unknown>): DoOption<T> {
const newOption = chain((scope: T) => map(() => scope)(fa))(this.option)
return new DoOption(newOption)
}
doL(f: (scope: T) => Option<unknown>): DoOption<T> {
const newOption = chain((scope: T) => map(() => scope)(f(scope)))(
this.option,
)
return new DoOption(newOption)
}
static start(): DoOption<{}> {
return new DoOption(some({}))
}
}