summaryrefslogtreecommitdiff
path: root/u/trace/itrace.ts
blob: e6189d3f8d1753e6c451831a585765ef2929d5aa (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
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
import type { Mapper, SideEffect, Supplier } from "@emprespresso/pengueno";

// the "thing" every Trace writer must "trace()"
type BaseTraceWith = string;
export type ITraceWith<T> = BaseTraceWith | T;
export interface ITrace<TraceWith> {
  addTrace: Mapper<ITraceWith<TraceWith>, ITrace<TraceWith>>;
  trace: SideEffect<ITraceWith<TraceWith>>;
}

export type ITraceableTuple<T, TraceWith> = [T, BaseTraceWith | TraceWith];
export type ITraceableMapper<
  T,
  U,
  TraceWith,
  W = ITraceable<T, TraceWith>,
> = (
  w: W,
) => U;

export interface ITraceable<T, Trace = BaseTraceWith> {
  readonly trace: ITrace<Trace>;
  get: Supplier<T>;
  move: <U>(u: U) => ITraceable<U, Trace>;
  map: <U>(
    mapper: ITraceableMapper<T, U, Trace>,
  ) => ITraceable<U, Trace>;
  bimap: <U>(
    mapper: ITraceableMapper<
      T,
      ITraceableTuple<U, Array<Trace> | Trace>,
      Trace
    >,
  ) => ITraceable<U, Trace>;
  peek: (peek: ITraceableMapper<T, void, Trace>) => ITraceable<T, Trace>;
  flatMap: <U>(
    mapper: ITraceableMapper<T, ITraceable<U, Trace>, Trace>,
  ) => ITraceable<U, Trace>;
  flatMapAsync<U>(
    mapper: ITraceableMapper<T, Promise<ITraceable<U, Trace>>, Trace>,
  ): ITraceable<Promise<U>, Trace>;
}

export class TraceableImpl<T, TraceWith> implements ITraceable<T, TraceWith> {
  protected constructor(
    private readonly item: T,
    public readonly trace: ITrace<TraceWith>,
  ) {}

  public map<U>(
    mapper: ITraceableMapper<T, U, TraceWith>,
  ) {
    const result = mapper(this);
    return new TraceableImpl(result, this.trace);
  }

  public flatMap<U>(
    mapper: ITraceableMapper<
      T,
      ITraceable<U, TraceWith>,
      TraceWith
    >,
  ): ITraceable<U, TraceWith> {
    return mapper(this);
  }

  public flatMapAsync<U>(
    mapper: ITraceableMapper<
      T,
      Promise<ITraceable<U, TraceWith>>,
      TraceWith
    >,
  ): ITraceable<Promise<U>, TraceWith> {
    return new TraceableImpl(
      mapper(this).then((t) => t.get()),
      this.trace,
    );
  }

  public peek(peek: ITraceableMapper<T, void, TraceWith>) {
    peek(this);
    return this;
  }

  public move<Tt>(t: Tt): ITraceable<Tt, TraceWith> {
    return this.map(() => t);
  }

  public bimap<U>(
    mapper: ITraceableMapper<
      T,
      ITraceableTuple<U, Array<TraceWith> | TraceWith>,
      TraceWith
    >,
  ) {
    const [item, trace] = mapper(this);
    const traces = Array.isArray(trace) ? trace : [trace];
    return new TraceableImpl(
      item,
      traces.reduce((trace, _trace) => trace.addTrace(_trace), this.trace),
    );
  }

  public get() {
    return this.item;
  }
}