import type { Mapper, SideEffect, Supplier } from '@emprespresso/pengueno'; /** * the "thing" every Trace writer must "trace()". */ type BaseTraceWith = string; export type ITraceWith = BaseTraceWith | T; export interface ITrace { /** * creates a new trace scope which inherits from this trace. */ traceScope: Mapper, ITrace>; /** * does the tracing. */ trace: SideEffect>; } export type ITraceableTuple = { item: T; trace: BaseTraceWith | TraceWith }; export type ITraceableMapper = (w: ITraceable) => _T; export interface ITraceable { readonly trace: ITrace; readonly get: Supplier; readonly move: <_T>(t: _T) => ITraceable<_T, Trace>; readonly map: <_T>(mapper: ITraceableMapper) => ITraceable<_T, Trace>; readonly bimap: <_T>(mapper: ITraceableMapper, Trace>) => ITraceable<_T, Trace>; readonly coExtend: <_T>( mapper: ITraceableMapper, Trace>, ) => ReadonlyArray>; readonly peek: (peek: ITraceableMapper) => ITraceable; readonly traceScope: (mapper: ITraceableMapper) => ITraceable; readonly flatMap: <_T>(mapper: ITraceableMapper, Trace>) => ITraceable<_T, Trace>; readonly flatMapAsync: <_T>( mapper: ITraceableMapper>, Trace>, ) => ITraceable, Trace>; } export class TraceableImpl implements ITraceable { protected constructor( private readonly item: T, public readonly trace: ITrace, ) {} public map<_T>(mapper: ITraceableMapper) { const result = mapper(this); return new TraceableImpl(result, this.trace); } public coExtend<_T>(mapper: ITraceableMapper, Trace>): ReadonlyArray> { const results = mapper(this); return Array.from(results).map((result) => this.move(result)); } public flatMap<_T>(mapper: ITraceableMapper, Trace>): ITraceable<_T, Trace> { return mapper(this); } public flatMapAsync<_T>( mapper: ITraceableMapper>, Trace>, ): ITraceable, Trace> { return new TraceableImpl( mapper(this).then((t) => t.get()), this.trace, ); } public traceScope(mapper: ITraceableMapper): ITraceable { return new TraceableImpl(this.get(), this.trace.traceScope(mapper(this))); } public peek(peek: ITraceableMapper) { peek(this); return this; } public move<_T>(t: _T): ITraceable<_T, Trace> { return this.map(() => t); } public bimap<_T>(mapper: ITraceableMapper, Trace>) { const { item, trace: _trace } = mapper(this); return this.move(item).traceScope(() => _trace); } public get() { return this.item; } }