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 { addTrace: Mapper, ITrace>; trace: SideEffect>; } export type ITraceableTuple = [T, BaseTraceWith | TraceWith]; export type ITraceableMapper> = ( w: W, ) => _T; export interface ITraceable { readonly trace: ITrace; get: Supplier; move: <_T>(t: _T) => ITraceable<_T, Trace>; map: <_T>(mapper: ITraceableMapper) => ITraceable<_T, Trace>; bimap: <_T>( mapper: ITraceableMapper< T, ITraceableTuple<_T, Array | Trace>, Trace >, ) => ITraceable<_T, Trace>; peek: (peek: ITraceableMapper) => ITraceable; flatMap: <_T>( mapper: ITraceableMapper, Trace>, ) => ITraceable<_T, Trace>; 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 flatMap<_T>( mapper: ITraceableMapper, TraceWith>, ): ITraceable<_T, TraceWith> { return mapper(this); } public flatMapAsync<_T>( mapper: ITraceableMapper>, TraceWith>, ): ITraceable, TraceWith> { return new TraceableImpl( mapper(this).then((t) => t.get()), this.trace, ); } public peek(peek: ITraceableMapper) { peek(this); return this; } public move<_T>(t: _T): ITraceable<_T, TraceWith> { return this.map(() => t); } public bimap<_T>( mapper: ITraceableMapper< T, ITraceableTuple<_T, Array | 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; } }