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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
import { BiMapper, IOptional, type Mapper, Optional, type Supplier, Tagged, isTagged } from '@emprespresso/pengueno';
export const IEitherTag = 'IEither' as const;
export type IEitherTag = typeof IEitherTag;
export const isEither = <E, T>(o: unknown): o is IEither<E, T> => isTagged(o, IEitherTag);
export interface IEither<E, T> extends Tagged<IEitherTag> {
readonly mapBoth: <_E, _T>(errBranch: Mapper<E, _E>, okBranch: Mapper<T, _T>) => IEither<_E, _T>;
readonly fold: <_T>(leftFolder: Mapper<E, _T>, rightFolder: Mapper<T, _T>) => _T;
readonly left: Supplier<IOptional<E>>;
readonly right: Supplier<IOptional<T>>;
readonly moveRight: <_T>(t: _T) => IEither<E, _T>;
readonly mapRight: <_T>(mapper: Mapper<T, _T>) => IEither<E, _T>;
readonly mapLeft: <_E>(mapper: Mapper<E, _E>) => IEither<_E, T>;
readonly flatMap: <_T>(mapper: Mapper<T, IEither<E, _T>>) => IEither<E, _T>;
readonly flatMapAsync: <_T>(mapper: Mapper<T, Promise<IEither<E, _T>>>) => Promise<IEither<E, _T>>;
}
const ELeftTag = 'E.Left' as const;
type ELeftTag = typeof ELeftTag;
export const isLeft = <E>(o: unknown): o is Left<E> => isTagged(o, ELeftTag);
interface Left<E> extends Tagged<ELeftTag> {
err: E;
}
const ERightTag = 'E.Right' as const;
type ERightTag = typeof ERightTag;
export const isRight = <T>(o: unknown): o is Right<T> => isTagged(o, ERightTag);
interface Right<T> extends Tagged<ERightTag> {
ok: T;
}
class _Tagged implements Tagged<IEitherTag> {
protected constructor(public readonly _tag = IEitherTag) {}
}
export class Either<E, T> extends _Tagged implements IEither<E, T> {
protected constructor(private readonly self: Left<E> | Right<T>) {
super();
}
public moveRight<_T>(t: _T) {
return this.mapRight(() => t);
}
public mapBoth<_E, _T>(errBranch: Mapper<E, _E>, okBranch: Mapper<T, _T>): IEither<_E, _T> {
if (isLeft(this.self)) return Either.left(errBranch(this.self.err));
return Either.right(okBranch(this.self.ok));
}
public mapRight<_T>(mapper: Mapper<T, _T>): IEither<E, _T> {
if (isRight(this.self)) return Either.right(mapper(this.self.ok));
return Either.left(this.self.err);
}
public mapLeft<_E>(mapper: Mapper<E, _E>): IEither<_E, T> {
if (isLeft(this.self)) return Either.left(mapper(this.self.err));
return Either.right(this.self.ok);
}
public flatMap<_T>(mapper: Mapper<T, IEither<E, _T>>): IEither<E, _T> {
if (isRight(this.self)) return mapper(this.self.ok);
return Either.left<E, _T>(this.self.err);
}
public async flatMapAsync<_T>(mapper: Mapper<T, Promise<IEither<E, _T>>>): Promise<IEither<E, _T>> {
if (isLeft(this.self)) return Promise.resolve(Either.left(this.self.err));
return await mapper(this.self.ok).catch((err) => Either.left(err));
}
public fold<_T>(leftFolder: Mapper<E, _T>, rightFolder: Mapper<T, _T>): _T {
if (isLeft(this.self)) return leftFolder(this.self.err);
return rightFolder(this.self.ok);
}
public left(): IOptional<E> {
if (isLeft(this.self)) return Optional.from(this.self.err) as IOptional<E>;
return Optional.none();
}
public right(): IOptional<T> {
if (isRight(this.self)) return Optional.from(this.self.ok) as IOptional<T>;
return Optional.none();
}
static joinRight<K, E, T>(
arr: Array<K>,
mapper: BiMapper<K, T, IEither<E, T>>,
init: IEither<E, T>,
): IEither<E, T> {
return arr.reduce((acc: IEither<E, T>, x: K) => acc.flatMap((t) => mapper(x, t)), init);
}
static joinRightAsync<K, E, T>(
arr: Array<K>,
mapper: BiMapper<K, T, Promise<IEither<E, T>>>,
init: IEither<E, T>,
): Promise<IEither<E, T>> {
return arr.reduce(
(acc: Promise<IEither<E, T>>, x: K) => acc.then((res) => res.flatMapAsync((t) => mapper(x, t))),
Promise.resolve(init),
);
}
static left<E, T>(e: E): IEither<E, T> {
return new Either({ err: e, _tag: ELeftTag });
}
static right<E, T>(t: T): IEither<E, T> {
return new Either({ ok: t, _tag: ERightTag });
}
static fromFailable<E, T>(s: Supplier<T>): IEither<E, T> {
try {
return Either.right(s());
} catch (e) {
return Either.left(e as E);
}
}
static async fromFailableAsync<E, T>(s: Supplier<Promise<T>> | Promise<T>): Promise<IEither<E, T>> {
return await (typeof s === 'function' ? s() : s)
.then((t: T) => Either.right<E, T>(t))
.catch((e: E) => Either.left<E, T>(e));
}
}
|