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

129 lines
3.7 KiB
TypeScript

import { bench, describe } from 'vitest';
import { STANDARD } from '../helpers/bench-options.js';
import {
okSome,
okNone,
errNone,
E,
O,
pipe,
} from '../helpers/imports.js';
// ---------------------------------------------------------------------------
// 5-step pipeline — OkSome path
// ---------------------------------------------------------------------------
describe('Query chaining — 5-step pipeline (all OkSome)', () => {
bench('native-monad', () => {
okSome(42)
.map(x => x * 2)
.flatMap(x => (x > 0 ? okSome(x) : okNone()))
.map(x => ({ value: x }))
.flatMap(x => okSome({ ...x, label: 'result' }))
.map(x => JSON.stringify(x));
}, STANDARD);
bench('fp-ts (nested)', () => {
pipe(
E.right(O.some(42)) as E.Either<string, O.Option<number>>,
E.map(O.map(x => x * 2)),
E.flatMap(inner =>
pipe(
inner,
O.match(
() => E.right(O.none) as E.Either<string, O.Option<number>>,
v => (v > 0 ? E.right(O.some(v)) : E.right(O.none)) as E.Either<string, O.Option<number>>,
),
),
),
E.map(O.map(x => ({ value: x }))),
E.flatMap(inner =>
pipe(
inner,
O.match(
() => E.right(O.none) as E.Either<string, O.Option<{ value: number; label: string }>>,
v => E.right(O.some({ ...v, label: 'result' })),
),
),
),
E.map(O.map(x => JSON.stringify(x))),
);
}, STANDARD);
});
// ---------------------------------------------------------------------------
// ErrNone short-circuit — 9 no-op maps
// ---------------------------------------------------------------------------
describe('Query chaining — ErrNone at step 1, 9 no-op maps', () => {
const nm = errNone('fail');
const fp = E.left('fail') as E.Either<string, O.Option<number>>;
const fn = (x: number) => x + 1;
bench('native-monad', () => {
nm.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
}, STANDARD);
bench('fp-ts (nested)', () => {
pipe(
fp,
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
);
}, STANDARD);
});
// ---------------------------------------------------------------------------
// OkNone short-circuit — 9 no-op maps
// ---------------------------------------------------------------------------
describe('Query chaining — OkNone, 9 no-op maps', () => {
const nm = okNone();
const fp = E.right(O.none) as E.Either<string, O.Option<number>>;
const fn = (x: number) => x + 1;
bench('native-monad', () => {
nm.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
}, STANDARD);
bench('fp-ts (nested)', () => {
pipe(
fp,
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
E.map(O.map(fn)),
);
}, STANDARD);
});
// ---------------------------------------------------------------------------
// 20-step deep map chain — OkSome path
// ---------------------------------------------------------------------------
describe('Query chaining — 20 maps on OkSome', () => {
const fn = (x: number) => x + 1;
bench('native-monad', () => {
let r = okSome(0);
for (let i = 0; i < 20; i++) r = r.map(fn);
}, STANDARD);
bench('fp-ts (nested)', () => {
let r = E.right(O.some(0)) as E.Either<string, O.Option<number>>;
for (let i = 0; i < 20; i++) r = pipe(r, E.map(O.map(fn)));
}, STANDARD);
});