initial commit

This commit is contained in:
2026-03-30 08:34:35 +02:00
commit 49d8ea67ba
103 changed files with 22387 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
import { bench, describe } from 'vitest';
import { EXTENDED } from '../helpers/bench-options.js';
import {
okSome,
okNone,
QueryPromise,
} from '../helpers/imports.js';
// ---------------------------------------------------------------------------
// QueryPromise.from — returns value
// ---------------------------------------------------------------------------
describe('QueryPromise.from — returns value', () => {
bench('native-monad', async () => {
await QueryPromise.from(() => Promise.resolve(42));
}, EXTENDED);
// No other library has a three-state async wrapper
});
// ---------------------------------------------------------------------------
// QueryPromise.from — returns null
// ---------------------------------------------------------------------------
describe('QueryPromise.from — returns null', () => {
bench('native-monad', async () => {
await QueryPromise.from(() => Promise.resolve(null));
}, EXTENDED);
});
// ---------------------------------------------------------------------------
// QueryPromise.all — all OkSome
// ---------------------------------------------------------------------------
for (const size of [10, 100] as const) {
describe(`QueryPromise.all — ${size} items, all OkSome`, () => {
const promises = () => Array.from({ length: size }, (_, i) => Promise.resolve(okSome(i)));
bench('native-monad', async () => {
await QueryPromise.all(promises());
}, EXTENDED);
});
}

View File

@@ -0,0 +1,128 @@
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);
});

View File

@@ -0,0 +1,144 @@
import { bench, describe } from 'vitest';
import { STANDARD, EXTENDED } from '../helpers/bench-options.js';
import { SIZES, errorIndices } from '../helpers/data-generators.js';
import {
okSome,
okNone,
errNone,
Query,
E,
O,
pipe,
} from '../helpers/imports.js';
import * as RA from 'fp-ts/ReadonlyArray';
// ---------------------------------------------------------------------------
// Query.all — all OkSome
// ---------------------------------------------------------------------------
for (const size of SIZES) {
const opts = size >= 10_000 ? EXTENDED : STANDARD;
const nmAll = Array.from({ length: size }, (_, i) => okSome(i));
const fpAll = Array.from({ length: size }, (_, i) =>
E.right(O.some(i)) as E.Either<string, O.Option<number>>,
);
describe(`Query.all — ${size} items, all OkSome`, () => {
bench('native-monad', () => { Query.all(nmAll); }, opts);
bench('fp-ts (nested, manual)', () => {
const values: number[] = [];
for (const item of fpAll) {
if (E.isLeft(item)) return item;
if (O.isNone(item.right)) continue;
values.push(item.right.value);
}
return E.right(O.some(values));
}, opts);
});
}
// ---------------------------------------------------------------------------
// Query.all — mixed (OkSome + OkNone + ErrNone)
// ---------------------------------------------------------------------------
for (const size of SIZES) {
const opts = size >= 10_000 ? EXTENDED : STANDARD;
const errIdx = errorIndices(size, 0.1);
const noneIdx = errorIndices(size, 0.15);
const nmMixed = Array.from({ length: size }, (_, i) => {
if (errIdx.has(i)) return errNone('fail');
if (noneIdx.has(i)) return okNone();
return okSome(i);
});
const fpMixed = Array.from({ length: size }, (_, i) => {
if (errIdx.has(i)) return E.left('fail') as E.Either<string, O.Option<number>>;
if (noneIdx.has(i)) return E.right(O.none) as E.Either<string, O.Option<number>>;
return E.right(O.some(i)) as E.Either<string, O.Option<number>>;
});
describe(`Query.all — ${size} items, mixed (short-circuit)`, () => {
bench('native-monad', () => { Query.all(nmMixed); }, opts);
bench('fp-ts (nested, manual)', () => {
const values: number[] = [];
for (const item of fpMixed) {
if (E.isLeft(item)) return item;
if (O.isNone(item.right)) continue;
values.push(item.right.value);
}
return E.right(O.some(values));
}, opts);
});
}
// ---------------------------------------------------------------------------
// Query.partition
// ---------------------------------------------------------------------------
for (const size of SIZES) {
const opts = size >= 10_000 ? EXTENDED : STANDARD;
const errIdx = errorIndices(size, 0.1);
const noneIdx = errorIndices(size, 0.15);
const nmMixed = Array.from({ length: size }, (_, i) => {
if (errIdx.has(i)) return errNone('fail');
if (noneIdx.has(i)) return okNone();
return okSome(i);
});
const fpMixed = Array.from({ length: size }, (_, i) => {
if (errIdx.has(i)) return E.left('fail') as E.Either<string, O.Option<number>>;
if (noneIdx.has(i)) return E.right(O.none) as E.Either<string, O.Option<number>>;
return E.right(O.some(i)) as E.Either<string, O.Option<number>>;
});
describe(`Query.partition — ${size} items`, () => {
bench('native-monad', () => { Query.partition(nmMixed); }, opts);
bench('fp-ts (nested, manual)', () => {
const values: number[] = [];
const errors: string[] = [];
let noneCount = 0;
for (const item of fpMixed) {
if (E.isLeft(item)) { errors.push(item.left); continue; }
if (O.isNone(item.right)) { noneCount++; continue; }
values.push(item.right.value);
}
return { values, noneCount, errors };
}, opts);
});
}
// ---------------------------------------------------------------------------
// Query.compact
// ---------------------------------------------------------------------------
for (const size of SIZES) {
const opts = size >= 10_000 ? EXTENDED : STANDARD;
const errIdx = errorIndices(size, 0.1);
const noneIdx = errorIndices(size, 0.15);
const nmMixed = Array.from({ length: size }, (_, i) => {
if (errIdx.has(i)) return errNone('fail');
if (noneIdx.has(i)) return okNone();
return okSome(i);
});
const fpMixed = Array.from({ length: size }, (_, i) => {
if (errIdx.has(i)) return E.left('fail') as E.Either<string, O.Option<number>>;
if (noneIdx.has(i)) return E.right(O.none) as E.Either<string, O.Option<number>>;
return E.right(O.some(i)) as E.Either<string, O.Option<number>>;
});
describe(`Query.compact — ${size} items`, () => {
bench('native-monad', () => { Query.compact(nmMixed); }, opts);
bench('fp-ts (nested, manual)', () => {
const values: number[] = [];
for (const item of fpMixed) {
if (E.isRight(item) && O.isSome(item.right)) values.push(item.right.value);
}
return values;
}, opts);
});
}

View File

@@ -0,0 +1,65 @@
import { bench, describe } from 'vitest';
import { STANDARD } from '../helpers/bench-options.js';
import {
okSome,
okNone,
errNone,
TsOk,
TsErr,
TsSome,
TsNone,
OxOk,
OxErr,
OxSome,
OxNone,
E,
O,
TmResult,
TmMaybe,
} from '../helpers/imports.js';
// ---------------------------------------------------------------------------
// OkSome — vs nested Result<Option<T>, E>
// ---------------------------------------------------------------------------
describe('Query construction — OkSome (primitive)', () => {
bench('native-monad', () => { okSome(42); }, STANDARD);
bench('ts-results-es (nested)', () => { TsOk(TsSome(42)); }, STANDARD);
bench('oxide.ts (nested)', () => { OxOk(OxSome(42)); }, STANDARD);
bench('fp-ts (nested)', () => { E.right(O.some(42)); }, STANDARD);
bench('true-myth (nested)', () => { TmResult.ok(TmMaybe.just(42)); }, STANDARD);
});
describe('Query construction — OkSome (object payload)', () => {
const value = { id: 1, name: 'Alice' };
bench('native-monad', () => { okSome(value); }, STANDARD);
bench('ts-results-es (nested)', () => { TsOk(TsSome(value)); }, STANDARD);
bench('oxide.ts (nested)', () => { OxOk(OxSome(value)); }, STANDARD);
bench('fp-ts (nested)', () => { E.right(O.some(value)); }, STANDARD);
bench('true-myth (nested)', () => { TmResult.ok(TmMaybe.just(value)); }, STANDARD);
});
// ---------------------------------------------------------------------------
// OkNone — vs nested Result<Option<T>, E>
// ---------------------------------------------------------------------------
describe('Query construction — OkNone (singleton)', () => {
bench('native-monad', () => { okNone(); }, STANDARD);
bench('ts-results-es (nested)', () => { TsOk(TsNone); }, STANDARD);
bench('oxide.ts (nested)', () => { OxOk(OxNone); }, STANDARD);
bench('fp-ts (nested)', () => { E.right(O.none); }, STANDARD);
bench('true-myth (nested)', () => { TmResult.ok(TmMaybe.nothing()); }, STANDARD);
});
// ---------------------------------------------------------------------------
// ErrNone — vs nested Err
// ---------------------------------------------------------------------------
describe('Query construction — ErrNone', () => {
bench('native-monad', () => { errNone('fail'); }, STANDARD);
bench('ts-results-es (nested)', () => { TsErr('fail'); }, STANDARD);
bench('oxide.ts (nested)', () => { OxErr('fail'); }, STANDARD);
bench('fp-ts (nested)', () => { E.left('fail'); }, STANDARD);
bench('true-myth (nested)', () => { TmResult.err('fail'); }, STANDARD);
});

View File

@@ -0,0 +1,172 @@
import { bench, describe } from 'vitest';
import { STANDARD } from '../helpers/bench-options.js';
import {
okSome,
okNone,
errNone,
E,
O,
pipe,
} from '../helpers/imports.js';
const double = (x: number) => x * 2;
const toUpper = (e: string) => e.toUpperCase();
// ---------------------------------------------------------------------------
// map — OkSome path
// ---------------------------------------------------------------------------
describe('Query.map — OkSome path', () => {
const nm = okSome(42);
const fp = E.right(O.some(42));
bench('native-monad', () => { nm.map(double); }, STANDARD);
bench('fp-ts (nested)', () => {
pipe(fp, E.map(O.map(double)));
}, STANDARD);
});
// ---------------------------------------------------------------------------
// map — OkNone path (no-op)
// ---------------------------------------------------------------------------
describe('Query.map — OkNone path (no-op)', () => {
const nm = okNone();
const fp = E.right(O.none);
bench('native-monad', () => { nm.map(double); }, STANDARD);
bench('fp-ts (nested)', () => {
pipe(fp, E.map(O.map(double)));
}, STANDARD);
});
// ---------------------------------------------------------------------------
// map — ErrNone path (no-op)
// ---------------------------------------------------------------------------
describe('Query.map — ErrNone path (no-op)', () => {
const nm = errNone('fail');
const fp = E.left('fail');
bench('native-monad', () => { nm.map(double); }, STANDARD);
bench('fp-ts (nested)', () => {
pipe(fp, E.map(O.map(double)));
}, STANDARD);
});
// ---------------------------------------------------------------------------
// flatMap — OkSome path
// ---------------------------------------------------------------------------
describe('Query.flatMap — OkSome path', () => {
const nm = okSome(42);
const fp = E.right(O.some(42));
bench('native-monad', () => { nm.flatMap(x => okSome(x * 2)); }, STANDARD);
bench('fp-ts (nested)', () => {
pipe(
fp,
E.flatMap(inner =>
pipe(
inner,
O.match(
() => E.right(O.none) as E.Either<string, O.Option<number>>,
v => E.right(O.some(v * 2)),
),
),
),
);
}, STANDARD);
});
// ---------------------------------------------------------------------------
// mapErr — ErrNone path
// ---------------------------------------------------------------------------
describe('Query.mapErr — ErrNone path', () => {
const nm = errNone('fail');
const fp = E.left('fail');
bench('native-monad', () => { nm.mapErr(toUpper); }, STANDARD);
bench('fp-ts (nested)', () => {
pipe(fp, E.mapLeft(toUpper));
}, STANDARD);
});
// ---------------------------------------------------------------------------
// match — three-way
// ---------------------------------------------------------------------------
describe('Query.match — OkSome (three-way dispatch)', () => {
const nm = okSome(42);
const fp = E.right(O.some(42));
bench('native-monad', () => {
nm.match(v => v * 2, () => 0, () => -1);
}, STANDARD);
bench('fp-ts (nested, two match calls)', () => {
pipe(
fp,
E.match(
() => -1,
O.match(() => 0, v => v * 2),
),
);
}, STANDARD);
});
describe('Query.match — OkNone (three-way dispatch)', () => {
const nm = okNone();
const fp = E.right(O.none);
bench('native-monad', () => {
nm.match(() => 1, () => 0, () => -1);
}, STANDARD);
bench('fp-ts (nested)', () => {
pipe(
fp,
E.match(
() => -1,
O.match(() => 0, () => 1),
),
);
}, STANDARD);
});
describe('Query.match — ErrNone (three-way dispatch)', () => {
const nm = errNone('fail');
const fp = E.left('fail');
bench('native-monad', () => {
nm.match(() => 1, () => 0, e => e.length);
}, STANDARD);
bench('fp-ts (nested)', () => {
pipe(
fp,
E.match(
e => e.length,
O.match(() => 0, () => 1),
),
);
}, STANDARD);
});
// ---------------------------------------------------------------------------
// filter — OkSome path
// ---------------------------------------------------------------------------
describe('Query.filter — predicate passes', () => {
const nm = okSome(42);
bench('native-monad', () => { nm.filter(x => x > 0); }, STANDARD);
// No other library has a three-state filter
});
describe('Query.filter — predicate fails', () => {
const nm = okSome(-1);
bench('native-monad', () => { nm.filter(x => x > 0); }, STANDARD);
});