1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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";
};
|