From d51c9d74857aca3c2f172609297266968bc7f809 Mon Sep 17 00:00:00 2001 From: Elizabeth Alexander Hunt Date: Mon, 12 May 2025 09:40:12 -0700 Subject: The big refactor TM --- u/fn/callable.ts | 22 +++++++++++++ u/fn/either.ts | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ u/fn/mod.ts | 2 ++ 3 files changed, 121 insertions(+) create mode 100644 u/fn/callable.ts create mode 100644 u/fn/either.ts create mode 100644 u/fn/mod.ts (limited to 'u/fn') diff --git a/u/fn/callable.ts b/u/fn/callable.ts new file mode 100644 index 0000000..fc6ea81 --- /dev/null +++ b/u/fn/callable.ts @@ -0,0 +1,22 @@ +// deno-lint-ignore no-explicit-any +export interface Callable { + (...args: Array): T; +} + +export interface Supplier extends Callable { + (): T; +} + +export interface Mapper extends Callable { + (t: T): U; +} + +export interface BiMapper extends Callable { + (t: T, u: U): R; +} + +export interface SideEffect extends Mapper { +} + +export interface BiSideEffect extends BiMapper { +} diff --git a/u/fn/either.ts b/u/fn/either.ts new file mode 100644 index 0000000..8b233bf --- /dev/null +++ b/u/fn/either.ts @@ -0,0 +1,97 @@ +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 ?? 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)); + } + 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"; +}; diff --git a/u/fn/mod.ts b/u/fn/mod.ts new file mode 100644 index 0000000..f0fbe88 --- /dev/null +++ b/u/fn/mod.ts @@ -0,0 +1,2 @@ +export * from "./callable.ts"; +export * from "./either.ts"; -- cgit v1.2.3-70-g09d2