Files
native-monad/benchmarks/result/collection.bench.ts
2026-03-30 08:34:35 +02:00

178 lines
6.1 KiB
TypeScript

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<number, string>(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<number, string>(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<number, string>('fail') : TsOk<number, string>(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<number, string>('fail') : TmResult.ok<number, string>(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);
});
}