initial commit
This commit is contained in:
70
benchmarks/result/async.bench.ts
Normal file
70
benchmarks/result/async.bench.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { EXTENDED } from '../helpers/bench-options.js';
|
||||
import { SIZES } from '../helpers/data-generators.js';
|
||||
import {
|
||||
ok,
|
||||
err,
|
||||
Result,
|
||||
ResultPromise,
|
||||
ntOk,
|
||||
ntErr,
|
||||
NtResultAsync,
|
||||
E,
|
||||
pipe,
|
||||
} from '../helpers/imports.js';
|
||||
import * as TE from 'fp-ts/TaskEither';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ResultPromise.from — success
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('ResultPromise.from — success', () => {
|
||||
bench('native-monad', async () => {
|
||||
await ResultPromise.from(() => Promise.resolve(42));
|
||||
}, EXTENDED);
|
||||
|
||||
bench('neverthrow', async () => {
|
||||
await NtResultAsync.fromPromise(Promise.resolve(42), () => 'err');
|
||||
}, EXTENDED);
|
||||
|
||||
bench('fp-ts', async () => {
|
||||
await TE.tryCatch(() => Promise.resolve(42), () => 'err')();
|
||||
}, EXTENDED);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ResultPromise.from — failure
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('ResultPromise.from — failure', () => {
|
||||
bench('native-monad', async () => {
|
||||
await ResultPromise.from(() => Promise.reject(new Error('fail')), () => 'err');
|
||||
}, EXTENDED);
|
||||
|
||||
bench('neverthrow', async () => {
|
||||
await NtResultAsync.fromPromise(Promise.reject(new Error('fail')), () => 'err');
|
||||
}, EXTENDED);
|
||||
|
||||
bench('fp-ts', async () => {
|
||||
await TE.tryCatch(() => Promise.reject(new Error('fail')), () => 'err')();
|
||||
}, EXTENDED);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ResultPromise.all — all Ok
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
for (const size of [10, 100] as const) {
|
||||
describe(`ResultPromise.all — ${size} items, all Ok`, () => {
|
||||
const nmPromises = () => Array.from({ length: size }, (_, i) => Promise.resolve(ok(i)));
|
||||
const ntPromises = () => Array.from({ length: size }, (_, i) => NtResultAsync.fromSafePromise(Promise.resolve(i)));
|
||||
|
||||
bench('native-monad', async () => {
|
||||
await ResultPromise.all(nmPromises());
|
||||
}, EXTENDED);
|
||||
|
||||
bench('neverthrow', async () => {
|
||||
await NtResultAsync.combine(ntPromises());
|
||||
}, EXTENDED);
|
||||
});
|
||||
}
|
||||
153
benchmarks/result/chaining.bench.ts
Normal file
153
benchmarks/result/chaining.bench.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { STANDARD } from '../helpers/bench-options.js';
|
||||
import {
|
||||
ok,
|
||||
err,
|
||||
ntOk,
|
||||
ntErr,
|
||||
TsOk,
|
||||
TsErr,
|
||||
OxOk,
|
||||
OxErr,
|
||||
E,
|
||||
pipe,
|
||||
TmResult,
|
||||
} from '../helpers/imports.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 5-step pipeline — success path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result chaining — 5-step pipeline (all succeed)', () => {
|
||||
bench('native-monad', () => {
|
||||
ok(42)
|
||||
.map(x => x * 2)
|
||||
.flatMap(x => (x > 0 ? ok(x) : err('negative')))
|
||||
.map(x => ({ value: x }))
|
||||
.flatMap(x => ok({ ...x, label: 'result' }))
|
||||
.map(x => JSON.stringify(x));
|
||||
}, STANDARD);
|
||||
|
||||
bench('neverthrow', () => {
|
||||
ntOk(42)
|
||||
.map(x => x * 2)
|
||||
.andThen(x => (x > 0 ? ntOk(x) : ntErr('negative')))
|
||||
.map(x => ({ value: x }))
|
||||
.andThen(x => ntOk({ ...x, label: 'result' }))
|
||||
.map(x => JSON.stringify(x));
|
||||
}, STANDARD);
|
||||
|
||||
bench('ts-results-es', () => {
|
||||
TsOk<number, string>(42)
|
||||
.map(x => x * 2)
|
||||
.andThen(x => (x > 0 ? TsOk(x) : TsErr('negative')))
|
||||
.map(x => ({ value: x }))
|
||||
.andThen(x => TsOk<{ value: number; label: string }, string>({ ...x, label: 'result' }))
|
||||
.map(x => JSON.stringify(x));
|
||||
}, STANDARD);
|
||||
|
||||
bench('oxide.ts', () => {
|
||||
OxOk(42)
|
||||
.map(x => x * 2)
|
||||
.andThen(x => (x > 0 ? OxOk(x) : OxErr('negative')))
|
||||
.map(x => ({ value: x }))
|
||||
.andThen(x => OxOk({ ...x, label: 'result' }))
|
||||
.map(x => JSON.stringify(x));
|
||||
}, STANDARD);
|
||||
|
||||
bench('fp-ts', () => {
|
||||
pipe(
|
||||
E.right(42),
|
||||
E.map(x => x * 2),
|
||||
E.flatMap(x => (x > 0 ? E.right(x) : E.left('negative'))),
|
||||
E.map(x => ({ value: x })),
|
||||
E.flatMap(x => E.right({ ...x, label: 'result' })),
|
||||
E.map(x => JSON.stringify(x)),
|
||||
);
|
||||
}, STANDARD);
|
||||
|
||||
bench('true-myth', () => {
|
||||
TmResult.ok<number, string>(42)
|
||||
.map(x => x * 2)
|
||||
.andThen(x => (x > 0 ? TmResult.ok(x) : TmResult.err('negative')))
|
||||
.map(x => ({ value: x }))
|
||||
.andThen(x => TmResult.ok({ ...x, label: 'result' }))
|
||||
.map(x => JSON.stringify(x));
|
||||
}, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 10-step pipeline — error at step 1 (9 no-ops)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result chaining — error at step 1, 9 no-op maps', () => {
|
||||
const nmErr = err('fail');
|
||||
const ntErrVal = ntErr('fail');
|
||||
const tsErr = TsErr<number, string>('fail');
|
||||
const oxErr = OxErr('fail');
|
||||
const fpErr = E.left('fail');
|
||||
const tmErr = TmResult.err<number, string>('fail');
|
||||
const fn = (x: number) => x + 1;
|
||||
|
||||
bench('native-monad', () => {
|
||||
nmErr.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
|
||||
}, STANDARD);
|
||||
|
||||
bench('neverthrow', () => {
|
||||
ntErrVal.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
|
||||
}, STANDARD);
|
||||
|
||||
bench('ts-results-es', () => {
|
||||
tsErr.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
|
||||
}, STANDARD);
|
||||
|
||||
bench('oxide.ts', () => {
|
||||
oxErr.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
|
||||
}, STANDARD);
|
||||
|
||||
bench('fp-ts', () => {
|
||||
pipe(fpErr, E.map(fn), E.map(fn), E.map(fn), E.map(fn), E.map(fn), E.map(fn), E.map(fn), E.map(fn), E.map(fn));
|
||||
}, STANDARD);
|
||||
|
||||
bench('true-myth', () => {
|
||||
tmErr.map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn).map(fn);
|
||||
}, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 20-step deep map chain — success path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result chaining — 20 maps on Ok', () => {
|
||||
const fn = (x: number) => x + 1;
|
||||
|
||||
bench('native-monad', () => {
|
||||
let r = ok(0);
|
||||
for (let i = 0; i < 20; i++) r = r.map(fn);
|
||||
}, STANDARD);
|
||||
|
||||
bench('neverthrow', () => {
|
||||
let r = ntOk(0);
|
||||
for (let i = 0; i < 20; i++) r = r.map(fn);
|
||||
}, STANDARD);
|
||||
|
||||
bench('ts-results-es', () => {
|
||||
let r = TsOk<number, string>(0);
|
||||
for (let i = 0; i < 20; i++) r = r.map(fn) as any;
|
||||
}, STANDARD);
|
||||
|
||||
bench('oxide.ts', () => {
|
||||
let r = OxOk(0);
|
||||
for (let i = 0; i < 20; i++) r = r.map(fn) as any;
|
||||
}, STANDARD);
|
||||
|
||||
bench('fp-ts', () => {
|
||||
let r = E.right(0) as E.Either<string, number>;
|
||||
for (let i = 0; i < 20; i++) r = pipe(r, E.map(fn));
|
||||
}, STANDARD);
|
||||
|
||||
bench('true-myth', () => {
|
||||
let r = TmResult.ok<number, string>(0);
|
||||
for (let i = 0; i < 20; i++) r = r.map(fn) as any;
|
||||
}, STANDARD);
|
||||
});
|
||||
177
benchmarks/result/collection.bench.ts
Normal file
177
benchmarks/result/collection.bench.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
59
benchmarks/result/construction.bench.ts
Normal file
59
benchmarks/result/construction.bench.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { STANDARD } from '../helpers/bench-options.js';
|
||||
import {
|
||||
ok,
|
||||
err,
|
||||
ntOk,
|
||||
ntErr,
|
||||
TsOk,
|
||||
TsErr,
|
||||
OxOk,
|
||||
OxErr,
|
||||
E,
|
||||
pipe,
|
||||
TmResult,
|
||||
} from '../helpers/imports.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ok / Right / success construction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result construction — Ok (primitive)', () => {
|
||||
bench('native-monad', () => { ok(42); }, STANDARD);
|
||||
bench('neverthrow', () => { ntOk(42); }, STANDARD);
|
||||
bench('ts-results-es', () => { TsOk(42); }, STANDARD);
|
||||
bench('oxide.ts', () => { OxOk(42); }, STANDARD);
|
||||
bench('fp-ts', () => { E.right(42); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.ok(42); }, STANDARD);
|
||||
});
|
||||
|
||||
describe('Result construction — Err (primitive)', () => {
|
||||
bench('native-monad', () => { err('fail'); }, STANDARD);
|
||||
bench('neverthrow', () => { ntErr('fail'); }, STANDARD);
|
||||
bench('ts-results-es', () => { TsErr('fail'); }, STANDARD);
|
||||
bench('oxide.ts', () => { OxErr('fail'); }, STANDARD);
|
||||
bench('fp-ts', () => { E.left('fail'); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.err('fail'); }, STANDARD);
|
||||
});
|
||||
|
||||
describe('Result construction — Ok (object payload)', () => {
|
||||
const value = { id: 1, name: 'Alice', roles: ['admin', 'user'] };
|
||||
|
||||
bench('native-monad', () => { ok(value); }, STANDARD);
|
||||
bench('neverthrow', () => { ntOk(value); }, STANDARD);
|
||||
bench('ts-results-es', () => { TsOk(value); }, STANDARD);
|
||||
bench('oxide.ts', () => { OxOk(value); }, STANDARD);
|
||||
bench('fp-ts', () => { E.right(value); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.ok(value); }, STANDARD);
|
||||
});
|
||||
|
||||
describe('Result construction — Err (Error object)', () => {
|
||||
const error = new Error('something went wrong');
|
||||
|
||||
bench('native-monad', () => { err(error); }, STANDARD);
|
||||
bench('neverthrow', () => { ntErr(error); }, STANDARD);
|
||||
bench('ts-results-es', () => { TsErr(error); }, STANDARD);
|
||||
bench('oxide.ts', () => { OxErr(error); }, STANDARD);
|
||||
bench('fp-ts', () => { E.left(error); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.err(error); }, STANDARD);
|
||||
});
|
||||
68
benchmarks/result/conversion.bench.ts
Normal file
68
benchmarks/result/conversion.bench.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { STANDARD } from '../helpers/bench-options.js';
|
||||
import {
|
||||
ok,
|
||||
err,
|
||||
ntOk,
|
||||
ntErr,
|
||||
TsOk,
|
||||
TsErr,
|
||||
OxOk,
|
||||
OxErr,
|
||||
E,
|
||||
pipe,
|
||||
TmResult,
|
||||
} from '../helpers/imports.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// toNullable — success path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.toNullable — Ok', () => {
|
||||
const nm = ok(42);
|
||||
const ox = OxOk(42);
|
||||
|
||||
bench('native-monad', () => { nm.toNullable(); }, STANDARD);
|
||||
// neverthrow: no toNullable — use match or _unsafeUnwrap
|
||||
// ts-results-es: no toNullable
|
||||
bench('oxide.ts', () => { ox.into(); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(E.right(42), E.toUnion); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.ok<number, string>(42).isOk ? 42 : null; }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// toNullable — failure path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.toNullable — Err', () => {
|
||||
const nm = err('fail');
|
||||
const ox = OxErr('fail');
|
||||
|
||||
bench('native-monad', () => { nm.toNullable(); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.into(); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(E.left('fail'), E.toUnion); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// toString
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.toString — Ok', () => {
|
||||
const nm = ok(42);
|
||||
const ox = OxOk(42);
|
||||
|
||||
bench('native-monad', () => { nm.toString(); }, STANDARD);
|
||||
// neverthrow: no toString
|
||||
// ts-results-es: no toString
|
||||
bench('oxide.ts', () => { ox.toString(); }, STANDARD);
|
||||
// fp-ts: no toString
|
||||
// true-myth: no toString
|
||||
});
|
||||
|
||||
describe('Result.toString — Err', () => {
|
||||
const nm = err('fail');
|
||||
const ox = OxErr('fail');
|
||||
|
||||
bench('native-monad', () => { nm.toString(); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.toString(); }, STANDARD);
|
||||
});
|
||||
39
benchmarks/result/from-wrapping.bench.ts
Normal file
39
benchmarks/result/from-wrapping.bench.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { STANDARD } from '../helpers/bench-options.js';
|
||||
import {
|
||||
Result,
|
||||
ntFromThrowable,
|
||||
E,
|
||||
TmResult,
|
||||
} from '../helpers/imports.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Result.from — success (no throw)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.from — success (no throw)', () => {
|
||||
const fn = () => 42;
|
||||
|
||||
bench('native-monad', () => { Result.from(fn); }, STANDARD);
|
||||
bench('neverthrow', () => { ntFromThrowable(fn)(); }, STANDARD);
|
||||
bench('fp-ts', () => { E.tryCatch(fn, () => 'error'); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.tryOr('error', fn); }, STANDARD);
|
||||
// ts-results-es: no from/tryCatch
|
||||
// oxide.ts: no from/tryCatch
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Result.from — failure (throws)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.from — failure (throws)', () => {
|
||||
const thrower = () => {
|
||||
throw new Error('fail');
|
||||
};
|
||||
const mapErr = () => 'error' as const;
|
||||
|
||||
bench('native-monad', () => { Result.from(thrower, mapErr); }, STANDARD);
|
||||
bench('neverthrow', () => { ntFromThrowable(thrower, mapErr)(); }, STANDARD);
|
||||
bench('fp-ts', () => { E.tryCatch(thrower, mapErr); }, STANDARD);
|
||||
bench('true-myth', () => { TmResult.tryOr('error', thrower); }, STANDARD);
|
||||
});
|
||||
208
benchmarks/result/instance-methods.bench.ts
Normal file
208
benchmarks/result/instance-methods.bench.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { STANDARD } from '../helpers/bench-options.js';
|
||||
import {
|
||||
ok,
|
||||
err,
|
||||
ntOk,
|
||||
ntErr,
|
||||
TsOk,
|
||||
TsErr,
|
||||
OxOk,
|
||||
OxErr,
|
||||
oxMatch,
|
||||
E,
|
||||
pipe,
|
||||
TmResult,
|
||||
} from '../helpers/imports.js';
|
||||
|
||||
const double = (x: number) => x * 2;
|
||||
const toUpper = (e: string) => e.toUpperCase();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// map — success path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.map — success path', () => {
|
||||
const nm = ok(42);
|
||||
const nt = ntOk(42);
|
||||
const ts = TsOk(42);
|
||||
const ox = OxOk(42);
|
||||
const fp = E.right(42);
|
||||
const tm = TmResult.ok<number, string>(42);
|
||||
|
||||
bench('native-monad', () => { nm.map(double); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.map(double); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.map(double); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.map(double); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.map(double)); }, STANDARD);
|
||||
bench('true-myth', () => { tm.map(double); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// map — failure path (no-op)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.map — failure path (no-op)', () => {
|
||||
const nm = err('fail');
|
||||
const nt = ntErr('fail');
|
||||
const ts = TsErr('fail');
|
||||
const ox = OxErr('fail');
|
||||
const fp = E.left('fail');
|
||||
const tm = TmResult.err<number, string>('fail');
|
||||
|
||||
bench('native-monad', () => { nm.map(double); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.map(double); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.map(double); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.map(double); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.map(double)); }, STANDARD);
|
||||
bench('true-myth', () => { tm.map(double); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// flatMap — success path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.flatMap — success path', () => {
|
||||
const nm = ok(42);
|
||||
const nt = ntOk(42);
|
||||
const ts = TsOk<number, string>(42);
|
||||
const ox = OxOk(42);
|
||||
const fp = E.right(42);
|
||||
const tm = TmResult.ok<number, string>(42);
|
||||
|
||||
bench('native-monad', () => { nm.flatMap(x => ok(x * 2)); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.andThen(x => ntOk(x * 2)); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.andThen(x => TsOk(x * 2)); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.andThen(x => OxOk(x * 2)); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.flatMap(x => E.right(x * 2))); }, STANDARD);
|
||||
bench('true-myth', () => { tm.andThen(x => TmResult.ok(x * 2)); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// flatMap — failure path (no-op)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.flatMap — failure path (no-op)', () => {
|
||||
const nm = err('fail');
|
||||
const nt = ntErr('fail');
|
||||
const ts = TsErr<number, string>('fail');
|
||||
const ox = OxErr('fail');
|
||||
const fp = E.left('fail');
|
||||
const tm = TmResult.err<number, string>('fail');
|
||||
|
||||
bench('native-monad', () => { nm.flatMap(x => ok(x * 2)); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.andThen(x => ntOk(x * 2)); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.andThen(x => TsOk(x * 2)); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.andThen(x => OxOk(x * 2)); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.flatMap(x => E.right(x * 2))); }, STANDARD);
|
||||
bench('true-myth', () => { tm.andThen(x => TmResult.ok(x * 2)); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// mapErr — error path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.mapErr — error path', () => {
|
||||
const nm = err('fail');
|
||||
const nt = ntErr('fail');
|
||||
const ts = TsErr<number, string>('fail');
|
||||
const ox = OxErr('fail');
|
||||
const fp = E.left('fail');
|
||||
const tm = TmResult.err<number, string>('fail');
|
||||
|
||||
bench('native-monad', () => { nm.mapErr(toUpper); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.mapErr(toUpper); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.mapErr(toUpper); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.mapErr(toUpper); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.mapLeft(toUpper)); }, STANDARD);
|
||||
bench('true-myth', () => { tm.mapErr(toUpper); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// mapErr — success path (no-op)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.mapErr — success path (no-op)', () => {
|
||||
const nm = ok(42);
|
||||
const nt = ntOk(42);
|
||||
const ts = TsOk<number, string>(42);
|
||||
const ox = OxOk(42);
|
||||
const fp = E.right(42);
|
||||
const tm = TmResult.ok<number, string>(42);
|
||||
|
||||
bench('native-monad', () => { nm.mapErr(toUpper); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.mapErr(toUpper); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.mapErr(toUpper); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.mapErr(toUpper); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.mapLeft(toUpper)); }, STANDARD);
|
||||
bench('true-myth', () => { tm.mapErr(toUpper); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// match — success path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.match — success path', () => {
|
||||
const nm = ok(42);
|
||||
const nt = ntOk(42);
|
||||
const ox = OxOk(42);
|
||||
const fp = E.right(42);
|
||||
const tm = TmResult.ok<number, string>(42);
|
||||
|
||||
bench('native-monad', () => { nm.match(x => x * 2, () => 0); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.match(x => x * 2, () => 0); }, STANDARD);
|
||||
// ts-results-es has no match — skip
|
||||
bench('oxide.ts', () => { oxMatch(ox, { Ok: (x: number) => x * 2, Err: () => 0 }); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.match(() => 0, x => x * 2)); }, STANDARD);
|
||||
bench('true-myth', () => { tm.match({ Ok: x => x * 2, Err: () => 0 }); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// match — failure path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.match — failure path', () => {
|
||||
const nm = err('fail');
|
||||
const nt = ntErr('fail');
|
||||
const ox = OxErr('fail');
|
||||
const fp = E.left('fail');
|
||||
const tm = TmResult.err<number, string>('fail');
|
||||
|
||||
bench('native-monad', () => { nm.match(() => 0, e => e.length); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.match(() => 0, e => e.length); }, STANDARD);
|
||||
bench('oxide.ts', () => { oxMatch(ox, { Ok: () => 0, Err: (e: string) => e.length }); }, STANDARD);
|
||||
bench('fp-ts', () => { pipe(fp, E.match(e => e.length, () => 0)); }, STANDARD);
|
||||
bench('true-myth', () => { tm.match({ Ok: () => 0, Err: e => e.length }); }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// filter — success path (predicate passes)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.filter — predicate passes', () => {
|
||||
const nm = ok(42);
|
||||
const fp = E.right(42);
|
||||
|
||||
bench('native-monad', () => { nm.filter(x => x > 0, () => 'negative'); }, STANDARD);
|
||||
// neverthrow: no filter — skip
|
||||
// ts-results-es: no filter — skip
|
||||
// oxide.ts: filter returns Option, not Result — different semantics, skip
|
||||
bench('fp-ts', () => {
|
||||
pipe(fp, E.filterOrElse(x => x > 0, () => 'negative' as const));
|
||||
}, STANDARD);
|
||||
// true-myth: no filter — skip
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// filter — predicate fails
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.filter — predicate fails', () => {
|
||||
const nm = ok(-1);
|
||||
const fp = E.right(-1);
|
||||
|
||||
bench('native-monad', () => { nm.filter(x => x > 0, () => 'negative'); }, STANDARD);
|
||||
bench('fp-ts', () => {
|
||||
pipe(fp, E.filterOrElse(x => x > 0, () => 'negative' as const));
|
||||
}, STANDARD);
|
||||
});
|
||||
74
benchmarks/result/type-guards.bench.ts
Normal file
74
benchmarks/result/type-guards.bench.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { STANDARD } from '../helpers/bench-options.js';
|
||||
import {
|
||||
ok,
|
||||
err,
|
||||
ntOk,
|
||||
ntErr,
|
||||
TsOk,
|
||||
TsErr,
|
||||
OxOk,
|
||||
OxErr,
|
||||
E,
|
||||
TmResult,
|
||||
} from '../helpers/imports.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// isOk — on Ok variant
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.isOk — on Ok', () => {
|
||||
const nm = ok(42);
|
||||
const nt = ntOk(42);
|
||||
const ts = TsOk(42);
|
||||
const ox = OxOk(42);
|
||||
const fp = E.right(42);
|
||||
const tm = TmResult.ok<number, string>(42);
|
||||
|
||||
bench('native-monad', () => { nm.isOk(); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.isOk(); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.isOk(); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.isOk(); }, STANDARD);
|
||||
bench('fp-ts', () => { E.isRight(fp); }, STANDARD);
|
||||
bench('true-myth', () => { tm.isOk; }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// isOk — on Err variant
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.isOk — on Err', () => {
|
||||
const nm = err('fail');
|
||||
const nt = ntErr('fail');
|
||||
const ts = TsErr('fail');
|
||||
const ox = OxErr('fail');
|
||||
const fp = E.left('fail');
|
||||
const tm = TmResult.err<number, string>('fail');
|
||||
|
||||
bench('native-monad', () => { nm.isOk(); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.isOk(); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.isOk(); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.isOk(); }, STANDARD);
|
||||
bench('fp-ts', () => { E.isRight(fp); }, STANDARD);
|
||||
bench('true-myth', () => { tm.isOk; }, STANDARD);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// isErr — on Err variant
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Result.isErr — on Err', () => {
|
||||
const nm = err('fail');
|
||||
const nt = ntErr('fail');
|
||||
const ts = TsErr('fail');
|
||||
const ox = OxErr('fail');
|
||||
const fp = E.left('fail');
|
||||
const tm = TmResult.err<number, string>('fail');
|
||||
|
||||
bench('native-monad', () => { nm.isErr(); }, STANDARD);
|
||||
bench('neverthrow', () => { nt.isErr(); }, STANDARD);
|
||||
bench('ts-results-es', () => { ts.isErr(); }, STANDARD);
|
||||
bench('oxide.ts', () => { ox.isErr(); }, STANDARD);
|
||||
bench('fp-ts', () => { E.isLeft(fp); }, STANDARD);
|
||||
bench('true-myth', () => { tm.isErr; }, STANDARD);
|
||||
});
|
||||
Reference in New Issue
Block a user