-
Notifications
You must be signed in to change notification settings - Fork 16
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
Revisiting mutable vs immutable APIs #93
Comments
I don't want to introduce the complexity of random-access methods cloning behind the scenes and forcing implementations to include special optimizations or suffer memory churn. Map and Set, along with their Weak counterparts, also implement a chainable "fluent" interface, as do Array methods such as ES1 I wanted the random-access methods to return iteration result objects (i.e., corresponding with |
Is that really the case? I would have imagined the mutability of the properties in play would generally preclude that. let a, b;
const { following } = Intl.Segmenter.prototype;
Intl.Segmenter.prototype.following = function() {
if (a) {
b = this;
} else {
a = this;
}
return following.call(this);
}
new Intl.Segmenter().segment(string).following().following();
console.log(a === b); // ??? |
Engines, as far as I understand, tend to use different code paths depending on whether or not the prototype are all built-ins. If you mutate the prototype, then you lose those optimizations.
The difference is that Map, Set, and Array are data structures, whereas a segment is a "thing". My intuition, and I believe the intuition shared by others, is that you have an expectation that a data structure can be mutable, but the expectation is different for nouns. |
I doubt it would be easy to optimize the allocations out. Engines have a lot of very specific work to optimize Array iteration; these things don't generalize automatically. The optimizations that @sffc is positing seem even more difficult. However, we can make the decision that we're OK with an API shape which will likely cause more allocations (Temporal seems to be going in this direction) for semantic reasons. If @sffc and @gibson042 want to champion a higher-level API that will likely have more allocations, I'm OK with the proposal moving in this direction. I don't have any data that allocation would be the bottleneck, and maybe I've been emphasizing this goal too much. I'm not sure what @sffc is proposing for what
|
@hagbard How would this decision influence OmnICU? |
Option 1 (current) Option 2 (state-confining) Option 2 seems better to me and less divergent from existing ECMA-262 iterators, I'm just not ready to adopt it unilaterally. This is marked as blocking Stage 3, but I'm still going to put advancement on the agenda for February. I will update slides accordingly if we can resolve this before the meeting. |
I'm not fully groking this having just dropped in, but the OmnICU model I've been envisioning would be something like what @littledan was saying (the stateless "segments" prototype would be what calls into WASM, passing position and string handle, and then updates the iterator on the host side with new position and updated state derived from the OmniICU result). This kind of thing is going to be hard to make super efficient whatever we (OmnICU) do, and maybe we want to look at higher level functionality too (e.g. "iterate to segment with property X" as higher level). |
Resolved in favor of adopting this suggestion (summarized as Option 2) by me and @sffc and @littledan. |
From January 27 phone call: adopt the option proposed by @littledan, and leave room for possible future extensions based on user demand. |
Also, include a |
I'm a bit concerned that patterns such as the following will produce hard-to-detect bugs.
If the methods on
%SegmentIterator%.prototype
returned semantically new objects, this problem would be solved, but the obvious problem with such behavior is whether it is efficiently implementable.I think maybe it is still efficient to implement. C++, and probably Rust, has the concept of an rvalue. Basically it means that if the receiver is going to be destroyed after the current function call, you can optimize by using move semantics instead of copy semantics. So, for example, if you have a call site such as,
since the object associated with the first
segment
is going out of scope, the engine can use an optimized code path to re-use the existing object. It is even easier to optimize when using tail calls:And for the main use case via the Iterator interface, the engine definitely will have enough information to optimize out the object cloning.
Thoughts?
The text was updated successfully, but these errors were encountered: