doc: add CompareOptions.filterer#18
Conversation
gibson042
left a comment
There was a problem hiding this comment.
I'm a little torn on this, but leaning towards opposition. The only advantage of passing in a predicate rather than applying it to iterator output is that it allows filtering to affect boolean output. But it comes at the cost of making that mode intertwine implementation code with user code, which almost always incurs a performance penalty. I think it's easy enough to get boolean output like const isDifferent = !!deviations.filter(filterer).next().value, and if that much ceremony is too unergonomic then we can push to fix it with a new iterator helper, e.g. const isDifferent = !deviations.filter(filterer).isEmpty().
|
|
||
| ```ts | ||
| type CompareOptions = { | ||
| filterer?: (a: unknown, b: unknown) => boolean, |
There was a problem hiding this comment.
What inputs are expected for a and b? If they're values in the object graph, then how does a filterer differentiate
- extra vs. missing vs. mismatch
- property descriptor
- prototype
or just in general, where in the graph its invocation relates to?
There was a problem hiding this comment.
Ah, yes, I suppose the type of A and B are not unknown: they're a primitive (or "special" key). And a couple things are missing.
So I guess the type would be more like:
| filterer?: (a: unknown, b: unknown) => boolean, | |
| filterer?: ( | |
| a: Primitive, | |
| b: Primitive, | |
| path: (string | Symbol)[], | |
| reason: 'extra' | 'missing' | 'mismatch', | |
| ) => boolean, |
For descriptor, I think we already covered this: A and/or B would have that special key in path (which was missing before, so that probably created the confusion).
RE the "special" path segment, what about a well-known symbol, like Symbol(Deviation:Descriptor) & Symbol(Deviation:Prototype) (or Symbol(DescriptorDeviation) & Symbol(PrototypeDeviation))? I think I'd prefer the : delimited version.
Thoughts on mismatch → unequal, equality, or maybe even the equality algo used like same-zero? mismatch seems a little "shit happened" to me 😅
There was a problem hiding this comment.
RE the "special" path segment, what about a well-known symbol, like
Symbol(Deviation:Descriptor)&Symbol(Deviation:Prototype)(orSymbol(DescriptorDeviation)&Symbol(PrototypeDeviation))? I think I'd prefer the:delimited version.
The special path segment can't be a symbol, because any such symbol could itself be an actual property key—e.g., { foo: { [Symbol(DescriptorDeviation)]: { value: 42 } } } would have an value-mode path like ["foo", Symbol(DescriptorDeviation), "value"] which could not be distinguished from a similar descriptor-mode path for { foo: 42 }.
Thoughts on
mismatch→unequal,equality, or maybe even the equality algo used likesame-zero?mismatchseems a little "shit happened" to me 😅
Yeah, I don't love "mismatch" either. "unequal" works for me.
There was a problem hiding this comment.
Symbols are unique though, so Symbol(foo) ≠ Symbol(foo). If the symbol we use is well-known, it can be checked and there is no collision.
This isn't about getting a boolean; it's purely about performance (and maybe a little bit about DX—but not related to boolean).
Before I opened this, I checked with Oli, who said this would likely have relatively significant perf benefits:
|
I'm extremely skeptical here. Is he saying that constructing an { actual, expected, path, reason } object is more expensive than calling a user-defined function with (actual, expected, path, reason) arguments??
Given how commonplace Trying to optimize performance at this stage just seems misguided. From my perspective, the only argument to be made for |
As noted by Justin in the May plenary, this could have quite some advantages (performance, such as avoiding the needless expense of constructing an Iterator just to immediately exclude all its items).