-
Notifications
You must be signed in to change notification settings - Fork 59
feat: replacing "tuple" implementation of Result with monomorphic object
#439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Benchmark Results
Performance regressions of 30% or more should be investigated, unless they were anticipated. Smaller regressions may be due to normal variability, as we don't use dedicated CI infrastructure. |
Result with monomorphic object.Result with monomorphic object.
Result with monomorphic object.Result with monomorphic object
|
Hi Jake, thanks for the PR! I'm leaning toward rejecting this change. Let me start with my biggest subjective objection: I find the array destructuring more ergonomic; and you get used to the error being the first element. I also think the performance boost of the object approach is over-emphasized. You're not using While object properties are easier to read than array indexed access, the idiomatic pattern is immediate array destructuring, which is plenty readable: const [error, value] = myResultFunc()
if (error) {
return doSomethingWithError(error)
}
doSomethingWithValue(value)As far as Curious to hear your thoughts. Thanks again for proposing this change. To be clear, I'm absolutely open to the idea, but I have my reservations. I'm also curious to hear what others have to say on this, if anyone else wants to chime in. ✌️ |
|
No worries! I understand if your vision of The tuple version with destructuring is more like Go than Rust and a lot of people (probably most) like that style more. I don't, but I also like Rust and have super strict TS linting turned on to be aggressively opinionated at compile time, so I already know my opinion on these things does not reflect the popular perspective. Otherwise, Radashi has been great to work with so far! |
|
And on the note of requiring // This non-Error type has all the real error info
type ValidationError =
| {
type: "tooLow";
minCutoff: number;
}
| {
type: "tooHigh";
maxCutoff: number;
};
// This wrapper Error adds nothing
class ValidationErrorClass extends Error {
constructor(public validationError: ValidationError) {
super(`Validation error: ${JSON.stringify(validationError)}`);
}
}And you can still enforce not having a type Result<TOk, TErr> = TErr extends undefined
? [never, TOk]
: [TErr, undefined] | [undefined, TOk];
type DumbResult = Result<number, undefined>; // type is `[never, number]` |
Summary
The
Resulttype is now a monomorphic object rather than a "tuple". This has performance, functionality, and ergonomic improvements.Reasons:
isResultOkorisResultErrwhen you already knew it was aResultwas very high and to be avoided.TOkorTErr. EvenResult<undefined, undefined>is now valid.result.ok,result.value,result.error) are more readable than array indicesSyntax Comparison:
And now, if you want, you can drop the heavier
Errorobject (which can make it 1000x slower than using number or string or other) and even usedundefinedfor yourTErr:Benchmarks:
I created a comparison benchmark test you can copy-paste into this repo to explore the results since benchmarks are finicky and easy to get wrong.
The results are better in every case for monomorphic objects over tuples. The improvement to create them is negligible (~5%) but the difference in checking success and getting values is ~2x faster than indexing into the tuple (and >300x faster than using
isResultOk/Err!), and ~4x faster to runisResult.Related issue, if any:
For any code change,
Does this PR introduce a breaking change?
Yes
All uses of the
_.Resultwill have to be updated to use the object definition instead of the tuple one.Bundle impact
src/async/defer.tssrc/async/parallel.tssrc/async/retry.tssrc/async/toResult.tssrc/async/tryit.tssrc/typed/err.tssrc/typed/isResult.tssrc/typed/ok.tsFootnotes
Function size includes the
importdependencies of the function. ↩