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

133 lines
3.8 KiB
TypeScript

import { bench, describe } from 'vitest';
import { STANDARD } from '../helpers/bench-options.js';
import {
some,
none,
TsSome,
TsNone,
OxSome,
OxNone,
O,
pipe,
TmMaybe,
} from '../helpers/imports.js';
// ---------------------------------------------------------------------------
// 5-step pipeline — Some path
// ---------------------------------------------------------------------------
describe('Option chaining — 5-step pipeline (all Some)', () => {
bench('native-monad', () => {
some(42)
.map(x => x * 2)
.flatMap(x => (x > 0 ? some(x) : none()))
.map(x => ({ value: x }))
.flatMap(x => some({ ...x, label: 'result' }))
.map(x => JSON.stringify(x));
}, STANDARD);
bench('ts-results-es', () => {
TsSome(42)
.map(x => x * 2)
.andThen(x => (x > 0 ? TsSome(x) : TsNone))
.map(x => ({ value: x }))
.andThen(x => TsSome({ ...x, label: 'result' }))
.map(x => JSON.stringify(x));
}, STANDARD);
bench('oxide.ts', () => {
OxSome(42)
.map(x => x * 2)
.andThen(x => (x > 0 ? OxSome(x) : OxNone))
.map(x => ({ value: x }))
.andThen(x => OxSome({ ...x, label: 'result' }))
.map(x => JSON.stringify(x));
}, STANDARD);
bench('fp-ts', () => {
pipe(
O.some(42),
O.map(x => x * 2),
O.flatMap(x => (x > 0 ? O.some(x) : O.none)),
O.map(x => ({ value: x })),
O.flatMap(x => O.some({ ...x, label: 'result' })),
O.map(x => JSON.stringify(x)),
);
}, STANDARD);
bench('true-myth', () => {
TmMaybe.just(42)
.map(x => x * 2)
.andThen(x => (x > 0 ? TmMaybe.just(x) : TmMaybe.nothing()))
.map(x => ({ value: x }))
.andThen(x => TmMaybe.just({ ...x, label: 'result' }))
.map(x => JSON.stringify(x));
}, STANDARD);
});
// ---------------------------------------------------------------------------
// None short-circuit — 9 no-op maps
// ---------------------------------------------------------------------------
describe('Option chaining — None at step 1, 9 no-op maps', () => {
const nmNone = none();
const tsNone = TsNone;
const oxNone = OxNone;
const fpNone = O.none;
const tmNone = TmMaybe.nothing<number>();
const fn = (x: number) => x + 1;
bench('native-monad', () => {
nmNone.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
}, STANDARD);
bench('ts-results-es', () => {
tsNone.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
}, STANDARD);
bench('oxide.ts', () => {
oxNone.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
}, STANDARD);
bench('fp-ts', () => {
pipe(fpNone, O.map(fn), O.map(fn), O.map(fn), O.map(fn), O.map(fn), O.map(fn), O.map(fn), O.map(fn), O.map(fn));
}, STANDARD);
bench('true-myth', () => {
tmNone.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
}, STANDARD);
});
// ---------------------------------------------------------------------------
// 20-step deep map chain — Some path
// ---------------------------------------------------------------------------
describe('Option chaining — 20 maps on Some', () => {
const fn = (x: number) => x + 1;
bench('native-monad', () => {
let r = some(0);
for (let i = 0; i < 20; i++) r = r.map(fn);
}, STANDARD);
bench('ts-results-es', () => {
let r = TsSome(0);
for (let i = 0; i < 20; i++) r = r.map(fn) as any;
}, STANDARD);
bench('oxide.ts', () => {
let r = OxSome(0);
for (let i = 0; i < 20; i++) r = r.map(fn) as any;
}, STANDARD);
bench('fp-ts', () => {
let r = O.some(0) as O.Option<number>;
for (let i = 0; i < 20; i++) r = pipe(r, O.map(fn));
}, STANDARD);
bench('true-myth', () => {
let r = TmMaybe.just(0);
for (let i = 0; i < 20; i++) r = r.map(fn) as any;
}, STANDARD);
});