import { bench, describe } from 'vitest'; import { STANDARD, EXTENDED } from '../helpers/bench-options.js'; import { SIZES, errorIndices } from '../helpers/data-generators.js'; import { ok, err, Result, ntOk, ntErr, NtResult, TsOk, TsErr, TsResult, OxOk, OxErr, E, pipe, TmResult, } from '../helpers/imports.js'; import * as RA from 'fp-ts/ReadonlyArray'; // --------------------------------------------------------------------------- // Result.all — all Ok // --------------------------------------------------------------------------- for (const size of SIZES) { const opts = size >= 10_000 ? EXTENDED : STANDARD; const nmAll = Array.from({ length: size }, (_, i) => ok(i)); const ntAll = Array.from({ length: size }, (_, i) => ntOk(i)); const tsAll = Array.from({ length: size }, (_, i) => TsOk(i)); const oxAll = Array.from({ length: size }, (_, i) => OxOk(i)); const fpAll = Array.from({ length: size }, (_, i) => E.right(i)); const tmAll = Array.from({ length: size }, (_, i) => TmResult.ok(i)); describe(`Result.all — ${size} items, all Ok`, () => { bench('native-monad', () => { Result.all(nmAll); }, opts); bench('neverthrow', () => { NtResult.combine(ntAll); }, opts); bench('ts-results-es', () => { TsResult.all(...tsAll); }, opts); // oxide.ts: no built-in all/combine bench('fp-ts', () => { pipe(fpAll, RA.sequence(E.Applicative)); }, opts); // true-myth: no built-in all bench('true-myth (manual)', () => { const values: number[] = []; for (const r of tmAll) { if (TmResult.isErr(r)) return r; values.push((r as { value: number }).value); } return TmResult.ok(values); }, opts); }); } // --------------------------------------------------------------------------- // Result.all — 20% Err (short-circuit) // --------------------------------------------------------------------------- for (const size of SIZES) { const opts = size >= 10_000 ? EXTENDED : STANDARD; const errIdx = errorIndices(size, 0.2); const nmMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? err('fail') : ok(i), ); const ntMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? ntErr('fail') : ntOk(i), ); const tsMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? TsErr('fail') : TsOk(i), ); const fpMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? E.left('fail' as const) : E.right(i), ); const tmMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? TmResult.err('fail') : TmResult.ok(i), ); describe(`Result.all — ${size} items, 20% Err (short-circuit)`, () => { bench('native-monad', () => { Result.all(nmMixed); }, opts); bench('neverthrow', () => { NtResult.combine(ntMixed as any); }, opts); bench('ts-results-es', () => { TsResult.all(...tsMixed); }, opts); bench('fp-ts', () => { pipe(fpMixed, RA.sequence(E.Applicative)); }, opts); bench('true-myth (manual)', () => { const values: number[] = []; for (const r of tmMixed) { if (TmResult.isErr(r)) return r; values.push((r as { value: number }).value); } return TmResult.ok(values); }, opts); }); } // --------------------------------------------------------------------------- // Result.partition // --------------------------------------------------------------------------- for (const size of SIZES) { const opts = size >= 10_000 ? EXTENDED : STANDARD; const errIdx = errorIndices(size, 0.2); const nmMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? err('fail') : ok(i), ); const ntMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? ntErr('fail') : ntOk(i), ); const fpMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? E.left('fail' as const) : E.right(i), ); describe(`Result.partition — ${size} items`, () => { bench('native-monad', () => { Result.partition(nmMixed); }, opts); bench('neverthrow (manual)', () => { const values: number[] = []; const errors: string[] = []; for (const r of ntMixed) { if (r.isOk()) values.push(r.value); else errors.push(r.error); } return { values, errors }; }, opts); bench('fp-ts', () => { RA.separate(fpMixed); }, opts); }); } // --------------------------------------------------------------------------- // Result.compact // --------------------------------------------------------------------------- for (const size of SIZES) { const opts = size >= 10_000 ? EXTENDED : STANDARD; const errIdx = errorIndices(size, 0.2); const nmMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? err('fail') : ok(i), ); const ntMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? ntErr('fail') : ntOk(i), ); describe(`Result.compact — ${size} items`, () => { bench('native-monad', () => { Result.compact(nmMixed); }, opts); bench('neverthrow (manual)', () => { const values: number[] = []; for (const r of ntMixed) { if (r.isOk()) values.push(r.value); } return values; }, opts); }); } // --------------------------------------------------------------------------- // Result.some / Result.every // --------------------------------------------------------------------------- for (const size of [100, 1_000]) { const errIdx = errorIndices(size, 0.2); const nmMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? err('fail') : ok(i), ); const ntMixed = Array.from({ length: size }, (_, i) => errIdx.has(i) ? ntErr('fail') : ntOk(i), ); describe(`Result.some — ${size} items`, () => { bench('native-monad', () => { Result.some(nmMixed); }, STANDARD); bench('neverthrow (manual)', () => { ntMixed.some(r => r.isOk()); }, STANDARD); }); describe(`Result.every — ${size} items`, () => { bench('native-monad', () => { Result.every(nmMixed); }, STANDARD); bench('neverthrow (manual)', () => { ntMixed.every(r => r.isOk()); }, STANDARD); }); }