summaryrefslogtreecommitdiff
path: root/u/fn/either.ts
blob: eaf77fd866444ab910bfc56c1fb2f3483e098d51 (plain)
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
import type { Mapper, Supplier } from "./mod.ts";

export interface IEither<E, T> {
  mapBoth: <Ee, Tt>(
    errBranch: Mapper<E, Ee>,
    okBranch: Mapper<T, Tt>,
  ) => IEither<Ee, Tt>;
  flatMap: <Tt>(mapper: Mapper<T, IEither<E, Tt>>) => IEither<E, Tt>;
  mapRight: <Tt>(mapper: Mapper<T, Tt>) => IEither<E, Tt>;
  mapLeft: <Ee>(mapper: Mapper<E, Ee>) => IEither<Ee, T>;
}

export class Either<E, T> implements IEither<E, T> {
  private constructor(private readonly err?: E, private readonly ok?: T) {}

  public mapBoth<Ee, Tt>(
    errBranch: Mapper<E, Ee>,
    okBranch: Mapper<T, Tt>,
  ): Either<Ee, Tt> {
    if (this.err) return Either.left(errBranch(this.err));
    return Either.right(okBranch(this.ok!));
  }

  public flatMap<Tt>(mapper: Mapper<T, Either<E, Tt>>) {
    if (this.ok) return mapper(this.ok);
    return Either.left<E, Tt>(this.err!);
  }

  public mapRight<Tt>(mapper: Mapper<T, Tt>): IEither<E, Tt> {
    if (this.ok) return Either.right(mapper(this.ok));
    return Either.left<E, Tt>(this.err!);
  }

  public mapLeft<Ee>(mapper: Mapper<E, Ee>) {
    if (this.err) return Either.left<Ee, T>(mapper(this.err));
    return Either.right<Ee, T>(this.ok!);
  }

  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);
    }
  }
}