diff options
author | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-12 09:40:12 -0700 |
---|---|---|
committer | Elizabeth <me@liz.coffee> | 2025-05-26 14:15:42 -0700 |
commit | d51c9d74857aca3c2f172609297266968bc7f809 (patch) | |
tree | 64327f9cc4219729aa11af32d7d4c70cddfc2292 /u/fn | |
parent | 30729a0cf707d9022bae0a7baaba77379dc31fd5 (diff) | |
download | ci-d51c9d74857aca3c2f172609297266968bc7f809.tar.gz ci-d51c9d74857aca3c2f172609297266968bc7f809.zip |
The big refactor TM
Diffstat (limited to 'u/fn')
-rw-r--r-- | u/fn/callable.ts | 22 | ||||
-rw-r--r-- | u/fn/either.ts | 97 | ||||
-rw-r--r-- | u/fn/mod.ts | 2 |
3 files changed, 121 insertions, 0 deletions
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<T = any, ArgT = any> { + (...args: Array<ArgT>): T; +} + +export interface Supplier<T> extends Callable<T, undefined> { + (): T; +} + +export interface Mapper<T, U> extends Callable<U, T> { + (t: T): U; +} + +export interface BiMapper<T, U, R> extends Callable { + (t: T, u: U): R; +} + +export interface SideEffect<T> extends Mapper<T, void> { +} + +export interface BiSideEffect<T, U> extends BiMapper<T, U, void> { +} 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<E, T> { + readonly _tag: IEitherTag; + mapBoth: <Ee, Tt>( + errBranch: Mapper<E, Ee>, + okBranch: Mapper<T, Tt>, + ) => IEither<Ee, Tt>; + fold: <Tt>(folder: BiMapper<E | undefined, T | undefined, Tt>) => Tt; + moveRight: <Tt>(t: Tt) => IEither<E, Tt>; + mapRight: <Tt>(mapper: Mapper<T, Tt>) => IEither<E, Tt>; + mapLeft: <Ee>(mapper: Mapper<E, Ee>) => IEither<Ee, T>; + flatMap: <Tt>(mapper: Mapper<T, IEither<E, Tt>>) => IEither<E, Tt>; + flatMapAsync: <Tt>( + mapper: Mapper<T, Promise<IEither<E, Tt>>>, + ) => Promise<IEither<E, Tt>>; +} + +export class Either<E, T> implements IEither<E, T> { + private constructor( + private readonly err?: E, + private readonly ok?: T, + public readonly _tag: IEitherTag = iEitherTag, + ) {} + + public moveRight<Tt>(t: Tt) { + return this.mapRight(() => t); + } + + public fold<R>(folder: BiMapper<E | undefined, T | undefined, R>): R { + return folder(this.err ?? undefined, this.ok ?? undefined); + } + + public mapBoth<Ee, Tt>( + errBranch: Mapper<E, Ee>, + okBranch: Mapper<T, Tt>, + ): Either<Ee, Tt> { + if (this.err !== undefined) return Either.left(errBranch(this.err)); + return Either.right(okBranch(this.ok!)); + } + + public flatMap<Tt>(mapper: Mapper<T, Either<E, Tt>>): Either<E, Tt> { + if (this.ok !== undefined) return mapper(this.ok); + return Either.left<E, Tt>(this.err!); + } + + public mapRight<Tt>(mapper: Mapper<T, Tt>): IEither<E, Tt> { + if (this.ok !== undefined) return Either.right<E, Tt>(mapper(this.ok)); + return Either.left<E, Tt>(this.err!); + } + + public mapLeft<Ee>(mapper: Mapper<E, Ee>): IEither<Ee, T> { + if (this.err !== undefined) return Either.left<Ee, T>(mapper(this.err)); + return Either.right<Ee, T>(this.ok!); + } + + public async flatMapAsync<Tt>( + mapper: Mapper<T, Promise<IEither<E, Tt>>>, + ): Promise<IEither<E, Tt>> { + if (this.err !== undefined) { + return Promise.resolve(Either.left<E, Tt>(this.err)); + } + return await mapper(this.ok!).catch((err) => Either.left<E, Tt>(err as E)); + } + + static left<E, T>(e: E) { + return new Either<E, T>(e); + } + + static right<E, T>(t: T) { + return new Either<E, T>(undefined, t); + } + + static fromFailable<E, T>(s: Supplier<T>) { + try { + return Either.right<E, T>(s()); + } catch (e) { + return Either.left<E, T>(e as E); + } + } + + static async fromFailableAsync<E, T>(s: Promise<T>) { + try { + return Either.right<E, T>(await s); + } catch (e) { + return Either.left<E, T>(e as E); + } + } +} + +export const isEither = <E, T>(o: unknown): o is IEither<E, T> => { + 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"; |