import type { BiMapper, Mapper, Supplier } from "@emprespresso/pengueno"; import { isObject } from "../leftpadesque/mod.ts"; type IEitherTag = "IEither"; const iEitherTag: IEitherTag = "IEither"; export interface IEither { readonly _tag: IEitherTag; mapBoth: ( errBranch: Mapper, okBranch: Mapper, ) => IEither; fold: (folder: BiMapper) => Tt; moveRight: (t: Tt) => IEither; mapRight: (mapper: Mapper) => IEither; mapLeft: (mapper: Mapper) => IEither; flatMap: (mapper: Mapper>) => IEither; flatMapAsync: ( mapper: Mapper>>, ) => Promise>; } export class Either implements IEither { private constructor( private readonly err?: E, private readonly ok?: T, public readonly _tag: IEitherTag = iEitherTag, ) {} public moveRight(t: Tt) { return this.mapRight(() => t); } public fold(folder: BiMapper): R { return folder(this.err ?? null, this.ok ?? null); } public mapBoth( errBranch: Mapper, okBranch: Mapper, ): Either { if (this.err !== undefined) return Either.left(errBranch(this.err)); return Either.right(okBranch(this.ok!)); } public flatMap(mapper: Mapper>): Either { if (this.ok !== undefined) return mapper(this.ok); return Either.left(this.err!); } public mapRight(mapper: Mapper): IEither { if (this.ok !== undefined) return Either.right(mapper(this.ok)); return Either.left(this.err!); } public mapLeft(mapper: Mapper): IEither { if (this.err !== undefined) return Either.left(mapper(this.err)); return Either.right(this.ok!); } public async flatMapAsync( mapper: Mapper>>, ): Promise> { if (this.err !== undefined) { return Promise.resolve(Either.left(this.err)); } return await mapper(this.ok!).catch((err) => Either.left(err as E)); } static left(e: E) { return new Either(e); } static right(t: T) { return new Either(undefined, t); } static fromFailable(s: Supplier) { try { return Either.right(s()); } catch (e) { return Either.left(e as E); } } static async fromFailableAsync(s: Promise) { try { return Either.right(await s); } catch (e) { return Either.left(e as E); } } } export const isEither = (o: unknown): o is IEither => { return isObject(o) && "_tag" in o && o._tag === "IEither"; };