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