From 98f5c21aa65bbbca01a186a754249335b4afef57 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Mon, 2 Jun 2025 16:52:52 -0700 Subject: fixup the Either monad a bit for type safetyp --- u/fn/either.ts | 183 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 102 insertions(+), 81 deletions(-) (limited to 'u/fn/either.ts') diff --git a/u/fn/either.ts b/u/fn/either.ts index b228af2..124557c 100644 --- a/u/fn/either.ts +++ b/u/fn/either.ts @@ -1,97 +1,118 @@ -import type { BiMapper, Mapper, Supplier } from "@emprespresso/pengueno"; -import { isObject } from "../leftpadesque/mod.ts"; +import { type Mapper, type Supplier, isObject } from "@emprespresso/pengueno"; type IEitherTag = "IEither"; const iEitherTag: IEitherTag = "IEither"; +export interface _Either { + readonly isLeft: LeftT; + readonly isRight: RightT; + readonly value: T; +} +export type Left = _Either; +export type Right = _Either; + export interface IEither { - readonly _tag: IEitherTag; - mapBoth: ( - errBranch: Mapper, - okBranch: Mapper, - ) => IEither; - fold: (folder: (err: E | undefined, val: T | undefined) => Tt) => Tt; //BiMapper) => Tt;; - moveRight: (t: Tt) => IEither; - mapRight: (mapper: Mapper) => IEither; - mapLeft: (mapper: Mapper) => IEither; - flatMap: (mapper: Mapper>) => IEither; - flatMapAsync: ( - mapper: Mapper>>, - ) => Promise>; + readonly _tag: IEitherTag; + + mapBoth: <_E, _T>( + errBranch: Mapper, + okBranch: Mapper, + ) => IEither<_E, _T>; + fold: <_T>(folder: Mapper | Right, _T>) => _T; + moveRight: <_T>(t: _T) => IEither; + mapRight: <_T>(mapper: Mapper) => IEither; + mapLeft: <_E>(mapper: Mapper) => IEither<_E, T>; + flatMap: <_T>(mapper: Mapper>) => IEither; + flatMapAsync: <_T>( + 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 ?? undefined, this.ok ?? undefined); - } - - 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)); + private readonly self: Left | Right; + + private constructor( + err?: E, + ok?: T, + public readonly _tag: IEitherTag = iEitherTag, + ) { + this.self = | Right>{ + isLeft: typeof err !== "undefined", + isRight: typeof ok !== "undefined", + value: typeof err !== "undefined" ? err : ok!, + }; + } + + public moveRight<_T>(t: _T) { + return this.mapRight(() => t); + } + + public fold<_T>(folder: Mapper | Right, _T>): _T { + return folder(this.self); + } + + public mapBoth<_E, _T>( + errBranch: Mapper, + okBranch: Mapper, + ): IEither<_E, _T> { + if (this.self.isLeft) return Either.left(errBranch(this.self.value)); + return Either.right(okBranch(this.self.value)); + } + + public flatMap<_T>(mapper: Mapper>): IEither { + if (this.self.isRight) return mapper(this.self.value); + return Either.left(this.self.value); + } + + public mapRight<_T>(mapper: Mapper): IEither { + if (this.self.isRight) + return Either.right(mapper(this.self.value)); + return Either.left(this.self.value); } - 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); + + public mapLeft<_E>(mapper: Mapper): IEither<_E, T> { + if (this.self.isLeft) + return Either.left<_E, T>(mapper(this.self.value)); + return Either.right<_E, T>(this.self.value); + } + + public async flatMapAsync<_T>( + mapper: Mapper>>, + ): Promise> { + if (this.self.isLeft) { + return Promise.resolve(Either.left(this.self.value)); + } + return await mapper(this.self.value).catch((err) => + Either.left(err), + ); + } + + static left(e: E): IEither { + return new Either(e, undefined); + } + static right(t: T): IEither { + return new Either(undefined, t); + } + + static fromFailable(s: Supplier): IEither { + 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); + static async fromFailableAsync( + s: Promise, + ): 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"; + return isObject(o) && "_tag" in o && o._tag === "IEither"; }; -- cgit v1.2.3-70-g09d2